{
 "openapi": "3.1.0",
 "info": {
  "title": "Human Handoff relay",
  "version": "1.0.0",
  "description": "Human-in-the-loop relay for AI agents. When your agent hits a CAPTCHA, a 2FA code, an approval or a sign-in it should not do alone, it calls request_human; your person gets a one-tap link, solves it in their own browser or pastes the code, and the answer comes back to your agent. Codes are read once and wiped in 10 minutes. We never solve CAPTCHAs and never ask for passwords. Free 500/day; Pro $9/month: https://pay.instaloops.com/b/6oU8wP9lK3wzf9tb8P83C3D"
 },
 "servers": [
  {
   "url": "https://relay.cloudforbots.com"
  }
 ],
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   }
  }
 },
 "paths": {
  "/api/run/request_human": {
   "post": {
    "operationId": "request_human",
    "summary": "Hand one blocked step to your human: a CAPTCHA, a 2FA/OTP code, an approval, or a sign-in your agent should not do alone",
    "description": "Hand one blocked step to your human: a CAPTCHA, a 2FA/OTP code, an approval, or a sign-in your agent should not do alone. Pro acts: sends your person a one-tap link (to notify_webhook, Slack/Discord-compatible, and/or notify_email), returns request_id, and later an HMAC-signed POST to callback_url when they answer. Free watch-only logs a would_request for the weekly stuck report and sends nothing. We never solve CAPTCHAs and never ask for passwords.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "kind": {
          "type": "string",
          "enum": [
           "captcha",
           "2fa_code",
           "approve",
           "login"
          ],
          "description": "captcha (person solves it in their browser), 2fa_code (person pastes the code; you read it once), approve (yes/no), login (person signs in, then taps done)."
         },
         "step": {
          "type": "string",
          "description": "One plain sentence for the person, e.g. \"Paste the 6-digit code GitHub just texted you\"."
         },
         "url": {
          "type": "string",
          "description": "The page the person should open, if any (https)."
         },
         "agent": {
          "type": "string",
          "description": "Your agent's name, shown to the person."
         },
         "notify_webhook": {
          "type": "string",
          "description": "Pro: public https URL we POST {text, human_url} to (Slack/Discord incoming webhooks work)."
         },
         "notify_email": {
          "type": "string",
          "description": "Pro: email address to send the one-tap link to."
         },
         "callback_url": {
          "type": "string",
          "description": "Pro: public https URL we POST the answer to, signed with X-Relay-Signature = hex HMAC-SHA256(callback_secret, timestamp + \".\" + body)."
         },
         "timeout_minutes": {
          "type": "integer",
          "description": "How long the person has, 1-1440. Default 30."
         }
        },
        "required": [
         "kind",
         "step"
        ]
       }
      }
     }
    },
    "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/check_request": {
   "post": {
    "operationId": "check_request",
    "summary": "Poll a request: pending, done, approved, denied, code_ready, expired or cancelled",
    "description": "Poll a request: pending, done, approved, denied, code_ready, expired or cancelled. For 2fa_code, the code is returned exactly once (single read) and only within 10 minutes of the person sending it; then it is wiped.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "request_id": {
          "type": "string"
         }
        },
        "required": [
         "request_id"
        ]
       }
      }
     }
    },
    "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/cancel_request": {
   "post": {
    "operationId": "cancel_request",
    "summary": "Cancel a pending request (for example your agent got past the step another way)",
    "description": "Cancel a pending request (for example your agent got past the step another way). The link stops working and any unread code is wiped.",
    "security": [
     {},
     {
      "bearer": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "request_id": {
          "type": "string"
         }
        },
        "required": [
         "request_id"
        ]
       }
      }
     }
    },
    "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": "Plain-English summary of the last 7 days: where your agent got stuck (by kind and by site), requests sent or that would have been, how many your person answered and how fast",
    "description": "Plain-English summary of the last 7 days: where your agent got stuck (by kind and by site), requests sent or that would have been, how many your person answered and how fast. Read-only; sends nothing. Needs a free API key.",
    "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"
     }
    }
   }
  }
 }
}