{
  "openapi": "3.1.0",
  "info": {
    "title": "Vibeflow API v0",
    "version": "0.1.0",
    "description": "Unified local + cloud task API. Locally the Phase 1 read alias (/api/v0, GET only) delegates to the local handler in localPath, and GET /api/docs renders this document. On the cloud surface, bearer calls carry per-token quotas — reads 300/min, writes 60/min, token creation 10/hour — and a quota-exceeding call answers 429 with Retry-After. Quota counters are PER-REPLICA in-memory counters, so the effective limit is the configured value × replicas (a shared store such as Redis is the future path if cross-replica enforcement is ever needed)."
  },
  "servers": [
    {
      "url": "/",
      "description": "Local board — try-it-out executes against this server"
    }
  ],
  "paths": {
    "/api/v0/tasks": {
      "get": {
        "operationId": "list-tasks",
        "summary": "List tasks with optional status filter and pagination",
        "description": "v0 returns task summary + counts with { items, nextCursor }; local returns { tasks: [...] } with commentCount/fileCount today.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:read",
        "x-vibeflow-local": "GET /api/tasks",
        "x-plan-row": 1,
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "backlog",
                "todo",
                "in-progress",
                "review",
                "done",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List tasks with optional status filter and pagination — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskList"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "items": [
                        {
                          "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                          "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                          "title": "Annotate the checkout button",
                          "status": "todo",
                          "commentCount": 2,
                          "fileCount": 1,
                          "createdAt": "2026-10-10T09:00:00.000Z"
                        }
                      ],
                      "nextCursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks?status=todo&limit=2"
        }
      },
      "post": {
        "operationId": "create-task",
        "summary": "Create a task",
        "description": "v0 makes 'selector' optional (annotation is local-only); local requires it today. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks",
        "x-plan-row": 3,
        "parameters": [],
        "responses": {
          "201": {
            "description": "Create a task — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo",
                      "priority": "high"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskCreate"
              },
              "examples": {
                "documented": {
                  "value": {
                    "title": "Annotate the checkout button",
                    "status": "todo",
                    "priority": "high"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks"
        }
      }
    },
    "/api/v0/tasks/{id}": {
      "get": {
        "operationId": "get-task",
        "summary": "Get one task by id",
        "description": "Cloud must add GET-by-id (PATCH-only today). Unknown id → 404; malformed id → 400 (local app.param guard).",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:read",
        "x-vibeflow-local": "GET /api/tasks/:id",
        "x-plan-row": 2,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get one task by id — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "description": "Button needs an accessible name",
                      "status": "in-progress",
                      "selector": "text=Checkout",
                      "priority": "high",
                      "verified": null,
                      "links": [],
                      "commentCount": 2,
                      "fileCount": 1,
                      "createdAt": "2026-10-10T09:00:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90"
        }
      },
      "patch": {
        "operationId": "patch-task",
        "summary": "Update a task",
        "description": "Unify the accepted field set with the cloud cliPatchSchema subset. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "PATCH /api/tasks/:id",
        "x-plan-row": 4,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Update a task — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "in-progress"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskPatch"
              },
              "examples": {
                "documented": {
                  "value": {
                    "status": "in-progress",
                    "priority": "high"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "PATCH",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90"
        }
      },
      "delete": {
        "operationId": "delete-task",
        "summary": "Delete a task (cascade)",
        "description": "Local cascade-deletes descendants + child links; cloud adapter must implement or v0 refuses (D7). Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "DELETE /api/tasks/:id",
        "x-plan-row": 5,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delete a task (cascade) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90"
        }
      }
    },
    "/api/v0/tasks/batch": {
      "post": {
        "operationId": "batch-create-tasks",
        "summary": "Create up to 100 tasks in one chunk",
        "description": "Ticket b4c525fa: { items: [...] } (max 100/req, named constant V0_BATCH_MAX_ITEMS) runs the single-create validation + quota + plan checks PER ITEM and returns per-item results [{ id } | { error: { code, message } }] in request order — one bad item never sinks the batch and the ordered array is the idMap the links phase needs. One multi-row insert per chunk; quota consumes per created item; quota-hit items fail per-item with 402 TASK_LIMIT. Runner marks NOT-RUNNABLE locally.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 3,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Create up to 100 tasks in one chunk — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskBatchResult"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "created": 2,
                      "failed": 0,
                      "results": [
                        {
                          "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90"
                        },
                        {
                          "id": "b2c3d4e5f60718293a4b5c6d7e8f90a1"
                        }
                      ],
                      "quota": {
                        "openRemaining": 498,
                        "totalRemaining": 1998
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskBatchCreate"
              },
              "examples": {
                "documented": {
                  "value": {
                    "items": [
                      {
                        "title": "Migrate checkout step 1"
                      },
                      {
                        "title": "Migrate checkout step 2",
                        "status": "todo"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/batch"
        }
      },
      "get": {
        "operationId": "batch-preflight",
        "summary": "Preflight: remaining task capacity before the first batch POST",
        "description": "Ticket b4c525fa: reports { maxItems, plan, open/total limit+used+remaining } so the CLI refuses-or-confirms before chunk one instead of discovering the cap mid-push. Scope is reads (probing must not require write); a 404 on old servers doubles as the CLI capability probe. Runner marks NOT-RUNNABLE locally.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:read",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 3,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Preflight: remaining task capacity before the first batch POST — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchPreflight"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "maxItems": 100,
                      "plan": "free",
                      "openLimit": 500,
                      "openUsed": 2,
                      "openRemaining": 498,
                      "totalLimit": 2000,
                      "totalUsed": 2,
                      "totalRemaining": 1998
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks/batch"
        }
      }
    },
    "/api/v0/tasks/{id}/opened": {
      "post": {
        "operationId": "mark-opened",
        "summary": "Record presence/telemetry open",
        "description": "Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/opened",
        "x-plan-row": 6,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Record presence/telemetry open — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/opened"
        }
      }
    },
    "/api/v0/tasks/{id}/expanded": {
      "post": {
        "operationId": "mark-expanded",
        "summary": "Record card expanded state",
        "description": "Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/expanded",
        "x-plan-row": 7,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Record card expanded state — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/expanded"
        }
      }
    },
    "/api/v0/tasks/{id}/links": {
      "post": {
        "operationId": "add-link",
        "summary": "Add a task link",
        "description": "New endpoint shape (biggest genuine gap). Locally links ride on create/update/detach today. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "PATCH /api/tasks/:id",
        "x-plan-row": 8,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "Add a task link — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TaskLinkInput"
              },
              "examples": {
                "documented": {
                  "value": {
                    "type": "blocks",
                    "taskId": "b2c3d4e5f60718293a4b5c6d7e8f90a1"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/links"
        }
      }
    },
    "/api/v0/tasks/{id}/links/{type}/{target}": {
      "delete": {
        "operationId": "remove-link",
        "summary": "Remove a task link",
        "description": "Local detach is the pre-image of the typed v0 removal. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/detach",
        "x-plan-row": 8,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "target",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Remove a task link — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "todo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/links/blocks/b2c3d4e5f60718293a4b5c6d7e8f90a1"
        }
      }
    },
    "/api/v0/tasks/claim-next": {
      "post": {
        "operationId": "claim-next",
        "summary": "Atomically claim the next actionable task",
        "description": "No local HTTP handler — local claim lives in core claimNextTaskAtomic + MCP claim_next_task. Cloud needs SELECT FOR UPDATE SKIP LOCKED (Phase 3). Runner marks NOT-RUNNABLE locally.",
        "tags": [
          "Agent"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 9,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Atomically claim the next actionable task — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimNext"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "task": {
                        "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                        "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                        "title": "Annotate the checkout button",
                        "status": "in-progress"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClaimFilter"
              },
              "examples": {
                "documented": {
                  "value": {
                    "status": "todo"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/claim-next"
        }
      }
    },
    "/api/v0/tasks/{id}/verify": {
      "post": {
        "operationId": "verify-task",
        "summary": "Attest a verify verdict (pass/fail/cannot)",
        "description": "No local HTTP handler — CLI 'vibeflow verify' + --set-verify + MCP verify_task. 'cannot' also writes the '**Cannot verify:**' comment. Runner marks NOT-RUNNABLE locally.",
        "tags": [
          "Agent"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 10,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Attest a verify verdict (pass/fail/cannot) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "externalId": "a1b2c3d4e5f60718293a4b5c6d7e8f90",
                      "title": "Annotate the checkout button",
                      "status": "review"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyAttestation"
              },
              "examples": {
                "documented": {
                  "value": {
                    "verdict": "pass",
                    "reason": "Checked against the baseline screenshot"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/verify"
        }
      }
    },
    "/api/v0/tasks/{id}/comments": {
      "get": {
        "operationId": "list-comments",
        "summary": "List comments",
        "description": "Direct match ({ comments: [...] }).",
        "tags": [
          "Comments"
        ],
        "x-vibeflow-scope": "comments:read",
        "x-vibeflow-local": "GET /api/tasks/:id/comments",
        "x-plan-row": 11,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List comments — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommentList"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "items": [
                        {
                          "id": "c0ffee1234567890",
                          "author": "user",
                          "text": "Reproduced on staging",
                          "type": "comment",
                          "source": "web",
                          "createdAt": "2026-10-10T10:00:00.000Z"
                        }
                      ],
                      "nextCursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/comments"
        }
      },
      "post": {
        "operationId": "add-comment",
        "summary": "Add a comment",
        "description": "Author semantics (L1): author preserved verbatim. Write — Phase 3.",
        "tags": [
          "Comments"
        ],
        "x-vibeflow-scope": "comments:write",
        "x-vibeflow-local": "POST /api/tasks/:id/comments",
        "x-plan-row": 11,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Add a comment — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comment"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "c0ffee1234567890",
                      "author": "agent",
                      "text": "Fix drafted, requesting review",
                      "type": "comment",
                      "source": "cli",
                      "createdAt": "2026-10-10T11:00:00.000Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentInput"
              },
              "examples": {
                "documented": {
                  "value": {
                    "author": "agent",
                    "text": "Fix drafted, requesting review"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/comments"
        }
      }
    },
    "/api/v0/tasks/{id}/comments/{commentId}": {
      "patch": {
        "operationId": "patch-comment",
        "summary": "Edit a comment",
        "description": "Cloud must add. Write — Phase 3.",
        "tags": [
          "Comments"
        ],
        "x-vibeflow-scope": "comments:write",
        "x-vibeflow-local": "PATCH /api/tasks/:id/comments/:commentId",
        "x-plan-row": 12,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Edit a comment — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Comment"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "c0ffee1234567890",
                      "author": "agent",
                      "text": "Fix drafted, requesting review (edited)"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "text"
                ]
              },
              "examples": {
                "documented": {
                  "value": {
                    "text": "Fix drafted, requesting review (edited)"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "PATCH",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/comments/c0ffee1234567890"
        }
      },
      "delete": {
        "operationId": "delete-comment",
        "summary": "Delete a comment",
        "description": "Cloud must add. Write — Phase 3.",
        "tags": [
          "Comments"
        ],
        "x-vibeflow-scope": "comments:write",
        "x-vibeflow-local": "DELETE /api/tasks/:id/comments/:commentId",
        "x-plan-row": 12,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "commentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delete a comment — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/comments/c0ffee1234567890"
        }
      }
    },
    "/api/v0/tasks/{id}/files": {
      "get": {
        "operationId": "list-files",
        "summary": "List files",
        "description": "Direct match ({ files: [...] }).",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "files:read",
        "x-vibeflow-local": "GET /api/tasks/:id/files",
        "x-plan-row": 13,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List files — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "items": [
                        {
                          "name": "note.txt",
                          "size": 42,
                          "mimeType": "text/plain",
                          "system": false
                        }
                      ],
                      "nextCursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/files"
        }
      },
      "post": {
        "operationId": "upload-file",
        "summary": "Upload a file",
        "description": "Both multipart and raw body accepted; residual divergence is the allowlist (extension vs MIME) + freemium checks, not the wire encoding. Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "files:write",
        "x-vibeflow-local": "POST /api/tasks/:id/files/:filename",
        "x-plan-row": 13,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Upload a file — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileEntry"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "name": "note.txt",
                      "size": 42,
                      "mimeType": "text/plain",
                      "system": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/files"
        }
      }
    },
    "/api/v0/tasks/{id}/files/{fileId}": {
      "get": {
        "operationId": "get-file",
        "summary": "Download a file",
        "description": "Match.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "files:read",
        "x-vibeflow-local": "GET /api/tasks/:id/files/:filename",
        "x-plan-row": 14,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File bytes",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/files/note.txt"
        },
        "x-vibeflow-example-note": "Binary download (application/octet-stream) — no JSON body to example."
      },
      "delete": {
        "operationId": "delete-file",
        "summary": "Delete a file",
        "description": "Match. Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "files:write",
        "x-vibeflow-local": "DELETE /api/tasks/:id/files/:filename",
        "x-plan-row": 14,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delete a file — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/files/note.txt"
        }
      }
    },
    "/api/v0/tasks/{id}/screenshot": {
      "post": {
        "operationId": "put-screenshot",
        "summary": "Attach a screenshot",
        "description": "Cloud stores as a system file. Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/screenshot",
        "x-plan-row": 15,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Attach a screenshot — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScreenshotResult"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true,
                      "screenshot": "a1b2c3d4e5f60718293a4b5c6d7e8f90.png"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScreenshotInput"
              },
              "examples": {
                "documented": {
                  "value": {
                    "screenshot": "<base64 PNG bytes>"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/screenshot"
        }
      },
      "delete": {
        "operationId": "delete-screenshot",
        "summary": "Remove the screenshot",
        "description": "Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "DELETE /api/tasks/:id/screenshot",
        "x-plan-row": 15,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Remove the screenshot — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/screenshot"
        }
      }
    },
    "/api/v0/tasks/{id}/baseline": {
      "post": {
        "operationId": "put-baseline",
        "summary": "Store baseline snapshot envelope",
        "description": "Merges the two local routes (baseline-element + baseline-page) into one envelope. The page-wide sibling handler POST /api/tasks/:id/baseline-page is classified under put-baseline-page. Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "files:write",
        "x-vibeflow-local": "POST /api/tasks/:id/baseline",
        "x-plan-row": 16,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store baseline snapshot envelope — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BaselineResult"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BaselineEnvelope"
              },
              "examples": {
                "documented": {
                  "value": {
                    "baseline": {
                      "selector": "text=Checkout",
                      "outerHTML": "<button>Checkout</button>"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/baseline"
        }
      }
    },
    "/api/v0/tasks/{id}/baseline-page": {
      "post": {
        "operationId": "put-baseline-page",
        "summary": "Store page-wide baseline snapshot as a system file",
        "description": "P3-A cutover: the second local baseline handler (POST /api/tasks/:id/baseline-page) keeps its own op — stores { page: { version: 1, elements, capturedAt, truncated } } as the baseline-page.json system file. Scope tasks:write per the published CLI online-mode call. Write — Phase 3.",
        "tags": [
          "Files"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/baseline-page",
        "x-plan-row": 16,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store page-wide baseline snapshot as a system file — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/baseline-page"
        }
      }
    },
    "/api/v0/tasks/{id}/auth-state": {
      "post": {
        "operationId": "put-auth-state",
        "summary": "Store encrypted auth state (LOCAL-ONLY)",
        "description": "Local-only concept (§2.5) — cloud answers 501 { code: LOCAL_ONLY }. Write — Phase 3.",
        "tags": [
          "Agent"
        ],
        "x-vibeflow-scope": "tasks:write",
        "x-vibeflow-local": "POST /api/tasks/:id/auth-state",
        "x-plan-row": 17,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Store encrypted auth state (LOCAL-ONLY) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthStateResult"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "501": {
            "description": "Local-only operation on the cloud surface",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthStateInput"
              },
              "examples": {
                "documented": {
                  "value": {
                    "authState": {
                      "cookies": []
                    },
                    "taskAuthor": "alice@example.com"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/auth-state"
        }
      }
    },
    "/api/v0/project": {
      "get": {
        "operationId": "get-project",
        "summary": "Describe the scope",
        "description": "Local { kind: local, name, branch }; cloud { kind: board, ... }. /api/github-url folds into this description.",
        "tags": [
          "Meta"
        ],
        "x-vibeflow-scope": "tasks:read",
        "x-vibeflow-local": "GET /api/project",
        "x-plan-row": 18,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Describe the scope — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "kind": "board",
                      "name": "Acme checkout",
                      "boardId": "board_01H",
                      "workspaceId": "ws_01H"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/project"
        }
      }
    },
    "/api/v0/me": {
      "get": {
        "operationId": "get-me",
        "summary": "Current principal",
        "description": "Match.",
        "tags": [
          "Meta"
        ],
        "x-vibeflow-scope": "profile:read",
        "x-vibeflow-local": "GET /api/user",
        "x-plan-row": 19,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Current principal — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Principal"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "id": "user_01H",
                      "name": "Ada",
                      "email": "ada@example.com",
                      "image": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/me"
        }
      }
    },
    "/api/v0/settings": {
      "get": {
        "operationId": "get-settings",
        "summary": "Read settings",
        "description": "Match.",
        "tags": [
          "Meta"
        ],
        "x-vibeflow-scope": "settings:read",
        "x-vibeflow-local": "GET /api/settings",
        "x-plan-row": 20,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Read settings — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "settings": {
                        "viewMode": "kanban",
                        "autoCommit": false
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/settings"
        }
      },
      "patch": {
        "operationId": "patch-settings",
        "summary": "Update settings",
        "description": "v0 uses PATCH; local uses POST today. Cloud must add PATCH. Write — Phase 3.",
        "tags": [
          "Meta"
        ],
        "x-vibeflow-scope": "settings:write",
        "x-vibeflow-local": "POST /api/settings",
        "x-plan-row": 20,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Update settings — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true,
                      "settings": {
                        "viewMode": "kanban",
                        "autoCommit": false
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Settable UI preference keys"
              },
              "examples": {
                "documented": {
                  "value": {
                    "viewMode": "kanban"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "PATCH",
          "path": "/api/v0/settings"
        }
      }
    },
    "/api/v0/changelog": {
      "get": {
        "operationId": "get-changelog",
        "summary": "CLI changelog (public read)",
        "description": "Public read, single implementation.",
        "tags": [
          "Meta"
        ],
        "x-vibeflow-scope": "none (public)",
        "x-vibeflow-local": "GET /api/changelog",
        "x-plan-row": 21,
        "parameters": [],
        "responses": {
          "200": {
            "description": "CLI changelog (public read) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Changelog"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "version": "1.2.3",
                      "sections": [
                        {
                          "version": "1.2.3",
                          "notes": [
                            "Fixed checkout annotation"
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/changelog"
        }
      }
    },
    "/api/v0/tasks/{id}/prompt": {
      "post": {
        "operationId": "export-prompt",
        "summary": "Export the structured agent prompt",
        "description": "MCP export_prompt REMOVED (ticket 53b622fc, BREAKING) — no MCP tool and no local handler remain. Endpoint unbuilt; runner marks NOT-RUNNABLE locally. Future v0 work must define its own shape.",
        "tags": [
          "Agent"
        ],
        "x-vibeflow-scope": "tasks:read",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 22,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Export the structured agent prompt — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PromptExport"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "prompt": "Implement: Annotate the checkout button (status todo) …"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PromptExportInput"
              },
              "examples": {
                "documented": {
                  "value": {
                    "scope": "task"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tasks/a1b2c3d4e5f60718293a4b5c6d7e8f90/prompt"
        }
      }
    },
    "/api/v0/tokens": {
      "get": {
        "operationId": "list-tokens",
        "summary": "List personal access tokens (prefix only, never secrets)",
        "description": "Plan §4.5 account endpoint (not a §1.2 task-surface row, hence planRow 0): cloud-only, no local handler — token management is online-only. Returns rows with prefix, name, scopes, lastUsedAt, expiresAt, revokedAt.",
        "tags": [
          "Auth"
        ],
        "x-vibeflow-scope": "tokens:manage",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [],
        "responses": {
          "200": {
            "description": "List personal access tokens (prefix only, never secrets) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenList"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "tokens": [
                        {
                          "id": "tok_01H",
                          "name": "CI runner",
                          "prefix": "vbf_pat_9f2c",
                          "scopes": [
                            "tasks:read",
                            "tasks:write"
                          ],
                          "createdAt": "2026-10-10T09:00:00.000Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/tokens"
        }
      },
      "post": {
        "operationId": "create-token",
        "summary": "Create a personal access token (secret returned once)",
        "description": "Plan §4.5: {name, scopes, expiresAt?} returns the plaintext vbf_pat_ secret exactly once, plus the prefix-only row. Session-authenticated for bootstrap, or bearer with tokens:manage.",
        "tags": [
          "Auth"
        ],
        "x-vibeflow-scope": "tokens:manage",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [],
        "responses": {
          "201": {
            "description": "Create a personal access token (secret returned once) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenSecret"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "token": "vbf_pat_9f2c_<redacted — returned once>",
                      "id": "tok_01H",
                      "name": "CI runner",
                      "prefix": "vbf_pat_9f2c",
                      "scopes": [
                        "tasks:read",
                        "tasks:write"
                      ],
                      "expiresAt": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenCreate"
              },
              "examples": {
                "documented": {
                  "value": {
                    "name": "CI runner",
                    "scopes": [
                      "tasks:read",
                      "tasks:write"
                    ]
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/tokens"
        }
      }
    },
    "/api/v0/tokens/{id}": {
      "delete": {
        "operationId": "revoke-token",
        "summary": "Revoke a personal access token (soft revoke, audit trail kept)",
        "description": "Plan §4.5/H1: sets revoked_at; api_tokens is the single authority so revocation is structurally authoritative — no fallback path can authenticate the string.",
        "tags": [
          "Auth"
        ],
        "x-vibeflow-scope": "tokens:manage",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoke a personal access token (soft revoke, audit trail kept) — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "success"
                  ]
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/tokens/tok_01H"
        }
      }
    },
    "/api/v0/boards": {
      "get": {
        "operationId": "list-boards",
        "summary": "List boards the principal is a member of",
        "description": "P3-A cutover: cloud-only, no local HTTP handler (local board listing lives in the workspace tRPC surface). Returns the member boards with the caller's role.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "boards:read",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [],
        "responses": {
          "200": {
            "description": "List boards the principal is a member of — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "items": [
                        {
                          "id": "board_01H",
                          "name": "Acme checkout",
                          "slug": "acme-checkout",
                          "role": "owner"
                        }
                      ],
                      "nextCursor": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/boards"
        }
      },
      "post": {
        "operationId": "create-board",
        "summary": "Create a board (caller becomes owner)",
        "description": "P3-A cutover: mirrors the workspace createWithBoard single source (board + owner membership + default project + kanban board) so a v0 board is immediately task-ready. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "boards:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [],
        "responses": {
          "201": {
            "description": "Create a board (caller becomes owner) — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "board": {
                        "id": "board_01H",
                        "name": "Acme checkout",
                        "slug": "acme-checkout",
                        "role": "owner"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/api/v0/boards"
        }
      }
    },
    "/api/v0/boards/{id}": {
      "get": {
        "operationId": "get-board",
        "summary": "Get one board by id",
        "description": "P3-A cutover: membership-scoped — a board the principal is not a member of is 404/403, never a leak. Unknown id → 404.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "boards:read",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Get one board by id — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "board": {
                        "id": "board_01H",
                        "name": "Acme checkout",
                        "slug": "acme-checkout",
                        "role": "owner"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/api/v0/boards/board_01H"
        }
      },
      "patch": {
        "operationId": "patch-board",
        "summary": "Update a board (name/icon/description)",
        "description": "P3-A cutover: same field set and NULL-vs-'' icon rule as the workspace update surface. Membership required; settings management may additionally require owner/admin per the role rule. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "boards:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Update a board (name/icon/description) — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "board": {
                        "id": "board_01H",
                        "name": "Acme checkout v2",
                        "slug": "acme-checkout",
                        "role": "owner"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "PATCH",
          "path": "/api/v0/boards/board_01H"
        }
      },
      "delete": {
        "operationId": "delete-board",
        "summary": "Delete a board (tasks cascade, never orphaned)",
        "description": "P3-A cutover: no session-side delete exists to mirror — deletion relies on the schema onDelete:cascade FKs (tasks, comments, files, kanban boards go with the board), the same cascade the task delete op documents per-row (D7). Membership required. Write — Phase 3.",
        "tags": [
          "Tasks"
        ],
        "x-vibeflow-scope": "boards:write",
        "x-vibeflow-local": "no local HTTP handler",
        "x-plan-row": 0,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Delete a board (tasks cascade, never orphaned) — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "success": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "DELETE",
          "path": "/api/v0/boards/board_01H"
        }
      }
    },
    "/mcp": {
      "get": {
        "operationId": "mcp-get",
        "summary": "Hosted MCP (StreamableHTTP session)",
        "description": "Local /api/mcp mounts POST|GET|DELETE; DELETE is session teardown (local-only extension, classified here). Phase 4 for hosted.",
        "tags": [
          "MCP"
        ],
        "x-vibeflow-scope": "token scopes",
        "x-vibeflow-local": "GET /api/mcp",
        "x-plan-row": 23,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Hosted MCP (StreamableHTTP session) — success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpSession"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "sessionId": "sess_01H",
                      "protocolVersion": "2025-06-18"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "GET",
          "path": "/mcp"
        }
      },
      "post": {
        "operationId": "mcp-post",
        "summary": "Hosted MCP call",
        "description": "See mcp-get. Phase 4 for hosted.",
        "tags": [
          "MCP"
        ],
        "x-vibeflow-scope": "token scopes",
        "x-vibeflow-local": "POST /api/mcp",
        "x-plan-row": 23,
        "parameters": [],
        "responses": {
          "200": {
            "description": "Hosted MCP call — success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "JSON-RPC response envelope"
                },
                "examples": {
                  "documented": {
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 1,
                      "result": {
                        "protocolVersion": "2025-06-18",
                        "capabilities": {},
                        "serverInfo": {
                          "name": "vibeflow",
                          "version": "1.0.0"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (includes invalid task/comment id form)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid principal",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Lacking scope or membership",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown id",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — cloud per-token quota exceeded (reads 300/min, writes 60/min, token creation 10/hour); counters are per-replica, so the effective limit is the configured value × replicas. The response carries Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-vibeflow-example-request": {
          "method": "POST",
          "path": "/mcp"
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "vbf_pat"
      }
    },
    "schemas": {
      "Task": {
        "type": "object",
        "description": "v0 canonical task = local Task fields plus external id, counts, and createdAt alias. D3: responses carry BOTH id and externalId.",
        "properties": {
          "id": {
            "type": "string",
            "description": "External id (cli_id ?? id on cloud; the local id locally)"
          },
          "externalId": {
            "type": "string",
            "description": "Universal external id — equal to id on the local surface"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "backlog",
              "todo",
              "in-progress",
              "review",
              "done",
              "cancelled"
            ]
          },
          "selector": {
            "type": "string"
          },
          "priority": {
            "type": "string"
          },
          "verified": {
            "type": "boolean",
            "nullable": true
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskLink"
            }
          },
          "commentCount": {
            "type": "integer"
          },
          "fileCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "externalId",
          "title",
          "status"
        ]
      },
      "TaskSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "externalId": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "backlog",
              "todo",
              "in-progress",
              "review",
              "done",
              "cancelled"
            ]
          },
          "commentCount": {
            "type": "integer"
          },
          "fileCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "externalId",
          "title",
          "status"
        ]
      },
      "TaskList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskSummary"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "items"
        ]
      },
      "TaskBatchCreate": {
        "type": "object",
        "description": "Body of POST /api/v0/tasks/batch (ticket b4c525fa): one chunk of single-create bodies (max 100/req — the server answers 400 BATCH_TOO_LARGE over the cap). Each item accepts the TaskCreate field set.",
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/TaskCreate"
            }
          },
          "boardId": {
            "type": "string"
          }
        },
        "required": [
          "items"
        ]
      },
      "TaskBatchResult": {
        "type": "object",
        "description": "Envelope of POST /api/v0/tasks/batch (ticket b4c525fa): ALWAYS 200 past request-level validation — per-item failures are data, never transport errors. `results` aligns to request order (the links-phase idMap); `quota` carries post-batch remaining capacity for the CLI chunk loop.",
        "properties": {
          "created": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id"
                  ]
                },
                {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              ]
            }
          },
          "quota": {
            "type": "object",
            "properties": {
              "openRemaining": {
                "type": "integer",
                "nullable": true
              },
              "totalRemaining": {
                "type": "integer",
                "nullable": true
              }
            }
          }
        },
        "required": [
          "created",
          "failed",
          "results"
        ]
      },
      "BatchPreflight": {
        "type": "object",
        "description": "Envelope of GET /api/v0/tasks/batch (ticket b4c525fa): remaining task capacity BEFORE the first batch POST. Limits (and remainings) are null when the plan caps nothing on that dimension.",
        "properties": {
          "maxItems": {
            "type": "integer"
          },
          "plan": {
            "type": "string"
          },
          "openLimit": {
            "type": "integer",
            "nullable": true
          },
          "openUsed": {
            "type": "integer"
          },
          "openRemaining": {
            "type": "integer",
            "nullable": true
          },
          "totalLimit": {
            "type": "integer",
            "nullable": true
          },
          "totalUsed": {
            "type": "integer"
          },
          "totalRemaining": {
            "type": "integer",
            "nullable": true
          }
        },
        "required": [
          "maxItems",
          "plan"
        ]
      },
      "TaskCreate": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "backlog",
              "todo",
              "in-progress",
              "review",
              "done",
              "cancelled"
            ]
          },
          "selector": {
            "type": "string",
            "description": "Optional in v0 (annotation is local-only); required by the local handler today"
          },
          "priority": {
            "type": "string"
          }
        },
        "required": [
          "title"
        ]
      },
      "TaskPatch": {
        "type": "object",
        "description": "All fields optional; status speaks the hyphenated set",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "backlog",
              "todo",
              "in-progress",
              "review",
              "done",
              "cancelled"
            ]
          },
          "priority": {
            "type": "string"
          },
          "verified": {
            "type": "boolean",
            "nullable": true
          }
        }
      },
      "TaskLink": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "parent",
              "relates",
              "blocks"
            ]
          }
        },
        "required": [
          "taskId",
          "type"
        ]
      },
      "TaskLinkInput": {
        "type": "object",
        "description": "Body of POST /api/v0/tasks/{id}/links. Field names match what BOTH implementations actually read (the cloud v0 route and the local /api/tasks/:id handler): `taskId` is the id of the task to link TO. The earlier `target` spelling was wrong — a client following it got a 400.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "parent",
              "relates",
              "blocks"
            ]
          },
          "taskId": {
            "type": "string",
            "description": "Id of the task to link to (external id on the cloud, local id locally)"
          }
        },
        "required": [
          "type",
          "taskId"
        ]
      },
      "ClaimFilter": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "backlog",
              "todo",
              "in-progress",
              "review",
              "done",
              "cancelled"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ClaimNext": {
        "type": "object",
        "properties": {
          "task": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Task"
              },
              {
                "type": "null",
                "description": "No actionable task"
              }
            ]
          }
        },
        "required": [
          "task"
        ]
      },
      "VerifyAttestation": {
        "type": "object",
        "description": "Agent attestation tri-state: pass/fail/cannot (+reason). 'cannot' also writes the Cannot-verify comment.",
        "properties": {
          "verdict": {
            "type": "string",
            "enum": [
              "pass",
              "fail",
              "cannot"
            ]
          },
          "reason": {
            "type": "string"
          }
        },
        "required": [
          "verdict"
        ]
      },
      "Comment": {
        "type": "object",
        "description": "Ledger L1: author is explicit and preserved end to end.",
        "properties": {
          "id": {
            "type": "string"
          },
          "author": {
            "type": "string",
            "enum": [
              "user",
              "agent"
            ]
          },
          "text": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "comment",
              "system"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "cli",
              "web"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "author",
          "text"
        ]
      },
      "CommentList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "items"
        ]
      },
      "CommentInput": {
        "type": "object",
        "properties": {
          "author": {
            "type": "string",
            "enum": [
              "user",
              "agent"
            ]
          },
          "text": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "text"
        ]
      },
      "FileEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "mimeType": {
            "type": "string"
          },
          "system": {
            "type": "boolean"
          }
        },
        "required": [
          "name"
        ]
      },
      "FileList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileEntry"
            }
          },
          "nextCursor": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "items"
        ]
      },
      "ScreenshotResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "screenshot": {
            "type": "string"
          }
        },
        "required": [
          "success"
        ]
      },
      "ScreenshotInput": {
        "type": "object",
        "properties": {
          "screenshot": {
            "type": "string",
            "contentEncoding": "base64"
          }
        },
        "required": [
          "screenshot"
        ]
      },
      "BaselineResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          }
        },
        "required": [
          "success"
        ]
      },
      "BaselineEnvelope": {
        "type": "object",
        "description": "Merges the two local baseline routes into one envelope",
        "properties": {
          "baseline": {
            "type": "object",
            "description": "Element snapshot { selector, outerHTML }"
          },
          "page": {
            "type": "object",
            "description": "Page-wide snapshot { version, elements, capturedAt }"
          }
        }
      },
      "AuthStateResult": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          }
        },
        "required": [
          "success"
        ]
      },
      "AuthStateInput": {
        "type": "object",
        "properties": {
          "authState": {
            "type": "object"
          },
          "taskAuthor": {
            "type": "string"
          }
        },
        "required": [
          "authState",
          "taskAuthor"
        ]
      },
      "Project": {
        "type": "object",
        "description": "Scope description: local { kind: local, name, branch }; cloud { kind: board, ... }",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "local",
              "board"
            ]
          },
          "name": {
            "type": "string"
          },
          "branch": {
            "type": "string",
            "nullable": true
          },
          "boardId": {
            "type": "string"
          },
          "workspaceId": {
            "type": "string"
          }
        },
        "required": [
          "kind"
        ]
      },
      "Principal": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "local",
              "user"
            ]
          },
          "userId": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "kind"
        ]
      },
      "Settings": {
        "type": "object",
        "description": "UI preference keys"
      },
      "Changelog": {
        "type": "object",
        "description": "CLI changelog payload"
      },
      "PromptExport": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string"
          }
        },
        "required": [
          "prompt"
        ]
      },
      "PromptExportInput": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "task",
              "board"
            ]
          }
        }
      },
      "McpSession": {
        "type": "object",
        "description": "StreamableHTTP session descriptor"
      },
      "Token": {
        "type": "object",
        "description": "Prefix-only token row — the secret is never re-revealable (plan §4.5).",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "Display prefix, e.g. vbf_pat_9f2c…"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lastUsedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "scopes"
        ]
      },
      "TokenList": {
        "type": "object",
        "properties": {
          "tokens": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Token"
            }
          }
        },
        "required": [
          "tokens"
        ]
      },
      "TokenCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "name",
          "scopes"
        ]
      },
      "TokenSecret": {
        "type": "object",
        "description": "Creation response — the secret appears here exactly once (plan §4.5).",
        "properties": {
          "token": {
            "type": "string",
            "description": "Full vbf_pat_ secret — shown once, never stored"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "token",
          "id",
          "name",
          "prefix",
          "scopes"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "details": {
            "type": "object"
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}
