{
 "openapi": "3.1.0",
 "info": {
  "title": "Handoff Watch",
  "version": "1.0.0",
  "description": "Stalled-handoff watcher for AI agents. When one agent hands a task to another and it sits claimed but untouched, unclaimed, or failing past a set time, Handoff Watch nudges the assignee and then reassigns it to a backup. Works alongside Offhand's shared task list and never against it. Free watch-only mode reports would_nudge; Pro at $9 a month acts. Free 500/day; Pro $9/month: https://pay.instaloops.com/b/fZudR9gOc4AD6CX1yf83C3A"
 },
 "servers": [
  {
   "url": "https://handoff.cloudforbots.com"
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   }
  }
 },
 "paths": {
  "/api/run/scan_handoffs": {
   "post": {
    "operationId": "scan_handoffs",
    "summary": "Pass the tasks your agents handed each other (e.g",
    "description": "Pass the tasks your agents handed each other (e.g. the result of Offhand list_todos) and get back which are stalled or failing and what to do. A task is stalled when it is claimed (in_progress) or handed over but untouched for stall_minutes, and failing when it is blocked or has failed fail_limit times. Ladder: nudge the assignee, nudge again, then reassign to a backup agent (or ask the person). Pro acts: records each nudge with a cooldown so nobody is pinged twice, and POSTs the nudge to the agent's webhook if you give one. Free watch-only takes no action and returns would_nudge / would_reassign. It never changes a task in Offhand itself and never takes a task another agent holds; reassigning returns an Offhand add_task call for your agent to make.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "board": {
          "type": "string",
          "description": "A stable name for this task list, e.g. \"offhand\" or \"team-ops\". Cooldowns and the escalation ladder are tracked per board."
         },
         "tasks": {
          "type": "array",
          "items": {
           "type": "object",
           "properties": {
            "id": {
             "type": "string"
            },
            "title": {
             "type": "string"
            },
            "status": {
             "type": "string",
             "description": "Offhand status: open, in_progress, blocked, done, need_review (also accepts todo, claimed, failed)."
            },
            "assignedTo": {
             "type": "string",
             "description": "Agent the task was handed to (alias: assignee)."
            },
            "updatedAt": {
             "type": "string",
             "description": "ISO time of the last status change or note (alias: updated_at)."
            },
            "idle_minutes": {
             "type": "number",
             "description": "Instead of updatedAt: minutes since the last activity."
            },
            "failures": {
             "type": "integer",
             "description": "Failed attempts so far (optional)."
            },
            "backup": {
             "type": "string",
             "description": "Agent that may take it over (optional)."
            }
           },
           "required": [
            "id"
           ]
          },
          "description": "Up to 200 tasks."
         },
         "stall_minutes": {
          "type": "integer",
          "description": "Minutes without activity before a task counts as stalled. Default 60."
         },
         "fail_limit": {
          "type": "integer",
          "description": "Failures before a task counts as failing. Default 2."
         },
         "nudges_before_reassign": {
          "type": "integer",
          "description": "Nudges before suggesting a reassign. Default 2."
         },
         "backup_agent": {
          "type": "string",
          "description": "Default agent to hand stalled work to."
         },
         "webhooks": {
          "type": "object",
          "description": "Optional map of agent name to an https URL that wakes it (Pro posts nudges there)."
         }
        },
        "required": [
         "tasks"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Result with job, understood, outcome, usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "bad_agent_key"
     },
     "429": {
      "description": "free_limit_reached (includes upgrade link)"
     }
    }
   }
  },
  "/api/run/nudge_log": {
   "post": {
    "operationId": "nudge_log",
    "summary": "List the latest nudges and reassigns (Pro) or would-nudges (free) for one board or all boards on your key.",
    "description": "List the latest nudges and reassigns (Pro) or would-nudges (free) for one board or all boards on your key.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "board": {
          "type": "string",
          "description": "A stable name for this task list, e.g. \"offhand\" or \"team-ops\". Cooldowns and the escalation ladder are tracked per board."
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Result with job, understood, outcome, usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "bad_agent_key"
     },
     "429": {
      "description": "free_limit_reached (includes upgrade link)"
     }
    }
   }
  },
  "/api/run/weekly_report": {
   "post": {
    "operationId": "weekly_report",
    "summary": "Draft a plain-English report of the last 7 days: stalled tasks found, nudges and reassigns made (Pro) or that would have been made (free)",
    "description": "Draft a plain-English report of the last 7 days: stalled tasks found, nudges and reassigns made (Pro) or that would have been made (free). Read-only; sends nothing.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Result with job, understood, outcome, usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "bad_agent_key"
     },
     "429": {
      "description": "free_limit_reached (includes upgrade link)"
     }
    }
   }
  },
  "/api/key": {
   "post": {
    "operationId": "get_free_key",
    "summary": "Get a free API key",
    "responses": {
     "200": {
      "description": "api_key"
     }
    }
   }
  }
 }
}