Lovely RouterДокументация
Получить ключ
Браузерный агент

Человек в контуре

Пусть агент задаёт вам вопросы, ждёт вашего подтверждения перед отправкой, передаёт вам браузер ради CAPTCHA и позволяет перехватить управление в любой момент.

Некоторым шагам нужен человек: одноразовый код, пришедший на ваш телефон, решение перед покупкой, CAPTCHA. Агент приостанавливает запуск, сообщает, что ему нужно, и продолжает после вашего ответа. Ваш код (или человек, который им пользуется) отвечает через один эндпоинт — POST /v1/browser/runs/{id}/reply.

Запуску нужноneeds.kindВы отвечаете
Информация, которой у него нетquestion{"answer": "…"} или {"secret": "…"}
Ваше согласие перед отправкойapproval{"approve": true} или {"approve": false, "reason": "…"}
Человек в браузереtakeoverЗавершите действие в Live View, затем {"done": true} или {"failed": true}

Как работает пауза#

Когда агенту нужны вы, status запуска становится waiting_for_human, а needs описывает, что требуется (в сокращении):

JSON
{
  "id": "brun_5b7d9f1a3c5e7a9b1d3f5a7c",
  "object": "browser_run",
  "status": "waiting_for_human",
  "steps": 6,
  "current_url": "https://bank.example.com/verify",
  "needs": {
    "kind": "question",
    "prompt": "What is the 6-digit code sent to your phone ending 42?",
    "expects_secret": true,
    "screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
    "since": "2026-10-11T09:31:12.000Z",
    "reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
  }
}
ПолеЗначение
needs.kindquestion, approval или takeover
needs.promptЧто нужно агенту, одним предложением, готовым для показа человеку
needs.screenshotПуть к JPEG-снимку страницы на момент начала паузы. Запрашивайте его со своим API-ключом.
needs.sinceКогда началась пауза
needs.replyПуть для ответа

Пока запуск ждёт:

  • Браузер остаётся открытым на той же странице, поэтому агент продолжает ровно с того места, где остановился.
  • Запрос статуса с ?wait= возвращается сразу, не удерживаясь. Ответьте или опрашивайте со своей задержкой.
  • Если вы настроили вебхук, он получает событие run.waiting.
  • Каждая пауза включает одну минуту. Сверх этого ожидание стоит $0.005 в минуту (цены).
  • Если никто не ответит в течение human_timeout_minutes (по умолчанию 30), запуск завершается статусом timed_out с ошибкой no human response within 30 minutes.

Обработка пауз в коде#

Этот цикл запускает задачу, которая может запросить подтверждение или код, и спрашивает человека в терминале:

import os
import requests

API = "https://lovelyrouter.com/v1/browser"
H = {"Authorization": f"Bearer {os.environ['LOVELY_API_KEY']}"}

run = requests.post(f"{API}/runs", headers=H, json={
    "goal": "In the rewards shop, redeem 500 points for the Blue Mug and tell me the order number.",
    "start_url": "https://shop.example.com/rewards",
    "profile": "shop-login",
    "approve": "risky",
}).json()

while run["status"] in ("running", "waiting_for_human"):
    if run["status"] == "waiting_for_human":
        needs = run["needs"]
        if needs["kind"] == "approval":
            print(needs["prompt"], *needs["fields"], sep="\n  ")
            reply = {"approve": input("Approve? [y/N] ").strip().lower() == "y"}
        elif needs["kind"] == "question":
            value = input(needs["prompt"] + " ")
            reply = {"secret": value} if needs["expects_secret"] else {"answer": value}
        else:  # takeover
            print(needs["prompt"], "\nOpen:", needs["live_view_url"])
            input("Press Enter when you are done. ")
            reply = {"done": True}
        # A 409 here means the run already moved on; the next status request shows where it is.
        requests.post(f"{API}/runs/{run['id']}/reply", headers=H, json=reply)
    run = requests.get(f"{API}/runs/{run['id']}", headers=H, params={"wait": 30}).json()

print(run["status"], run["answer"])

Если человек не сидит за терминалом, отправьте ему needs.prompt (и скриншот или ссылку на Live View) через своё приложение, чат или email и отправьте его ответ, когда он придёт. Когда это делать, подскажет вебхук.

Ответ на вопрос#

Агент спрашивает, когда странице нужно то, чего нет в цели: код подтверждения, пришедший на телефон или email, недостающая деталь или выбор, который можете сделать только вы. Ответить можно двумя способами:

  • {"answer": "…"} — текст, который агент читает и использует, до 2000 символов. Подходит для деталей и выбора: {"answer": "Use the billing address, not the shipping one."}
  • {"secret": "…"} — значение, которое агент вводит на страницу, никогда его не видя, до 4096 символов. Подходит для одноразовых кодов: {"secret": "481923"}

needs.expects_secret равен true, когда вопрос касается кода, пароля, PIN-кода или одноразового кода. Секретный ответ можно ввести только на том сайте, где был запуск в момент вопроса, в любое текстовое поле этого сайта, включая поля пароля и PIN-кода. Как и другие секреты, значение длиной от шести символов заменяется на [secret:<name>] везде, где оно появилось бы в выводе запуска, а само значение удаляется по завершении запуска. Пароли, которые нужны в каждом запуске, лучше сохранить в хранилище, чтобы агенту не приходилось спрашивать.

cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"secret": "481923"}'

Для кодов из приложения-аутентификатора лучше сохраните в хранилище TOTP-сид. Тогда агент сам вводит текущий код, и спрашивать ему не нужно. См. Коды двухфакторной аутентификации.

Подтверждение перед отправкой#

approve определяет, когда агент останавливается и ждёт вашего согласия перед действием, которое что-то отправляет: кнопкой, отправляющей форму, кнопкой с подписью о покупке, бронировании, оплате, сообщении или удалении («Buy now», «Book», «Pay», «Send», «Delete» и подобные), или нажатием Enter в поле формы, которая не является строкой поиска, — это отправляет форму. На ссылках агент никогда не останавливается: переход по ссылке лишь открывает страницу.

approveАгент останавливается перед
risky (по умолчанию)Такими действиями, если считает их необратимыми или значимыми: оплата или покупка, заказ или бронирование, отправка сообщения или письма, удаление данных, подача заявки. Вход в аккаунт, поиск, навигация и промежуточные шаги формы выполняются без остановки.
submitКаждым из таких действий.
noneНичем. Агент отправляет всё сам.

Любое другое значение отклоняется с 400. Пауза для подтверждения выглядит так:

JSON
{
  "status": "waiting_for_human",
  "needs": {
    "kind": "approval",
    "prompt": "Approve: click button \"Redeem 500 points\" on Blue Mug – Rewards?",
    "fields": ["Quantity: \"1\"", "Ship to: \"Ada Lovelace, 12 Main St\"", "Gift wrap: unchecked"],
    "action": "Redeem 500 points",
    "screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
    "since": "2026-10-11T09:33:40.000Z",
    "reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
  }
}

fields перечисляет текущие значения формы, а action — подпись кнопки, которую агент хочет нажать. Ответьте:

  • {"approve": true} — агент нажимает эту кнопку следующим шагом.
  • {"approve": false, "reason": "Wrong address"} — агент ничего не отправляет. Он читает вашу причину (до 500 символов) и пробует другой путь либо завершает работу и сообщает почему. {"deny": true} работает так же.
cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"approve": true}'

Передача управления ради CAPTCHA#

Агент не умеет решать CAPTCHA и проверки «подтвердите, что вы человек». Когда страница показывает такую проверку или шаг, который может выполнить только человек, агент передаёт вам браузер, и needs.kind становится takeover:

JSON
{
  "status": "waiting_for_human",
  "needs": {
    "kind": "takeover",
    "prompt": "Please solve the CAPTCHA and press Continue.",
    "live_view_url": "https://…",
    "live_view_expires_at": "2026-10-11T10:35:02.000Z",
    "screenshot": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/screenshot",
    "since": "2026-10-11T09:35:02.000Z",
    "reply": "/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/reply"
  }
}
  1. Откройте live_view_url в браузере. Там в реальном времени видна вкладка браузера агента, и человек может кликать и вводить текст. Ссылка действует один час.
  2. Человек делает то, что сказано в prompt.
  3. Ответьте {"done": true}, при желании добавив note (до 500 символов), которую прочитает агент, например "Solved the CAPTCHA, now on the search results". Если человек не справился, ответьте {"failed": true, "note": "…"}.

Если запуск сам замечает, что передача управления закончилась, он продолжает без вашего ответа, а более поздний ответ вернёт 409 — его можно игнорировать. Агент также передаёт управление, когда застревает: после трёх неудачных шагов подряд или после трёх повторов одного и того же действия без продвижения.

Страница с CAPTCHA или проверкой «подтвердите, что вы человек» передаётся человеку не более двух раз за запуск. Если после этого она всё ещё на месте или человек ответил {"failed": true}, запуск завершается статусом failed с ошибкой blocked by the website. То же происходит со страницей, которая полностью блокирует браузер (доступ запрещён, 403), ведь человек тоже не сможет её обойти, и с любой проверкой на человека, если запуск стартовал с "human": false.

Перехват управления вручную#

Вы можете забрать браузер в любой момент, например чтобы показать агенту дорогу или выполнить шаг вручную:

cURL
curl https://lovelyrouter.com/v1/browser/runs/brun_5b7d9f1a3c5e7a9b1d3f5a7c/takeover \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"instructions": "I will pick the delivery slot myself."}'

Запрос сразу возвращает 202. Запуск приостанавливается после текущего шага, и следующий статус показывает needs.kind takeover с live_view_url. Сделайте нужное в Live View, затем верните управление ответом {"done": true, "note": "Picked Saturday 10–12"}. Агент продолжит со страницы, на которой вы его оставили.

instructions (необязательно, до 500 символов) становится prompt паузы. Если запуск уже ждёт ответа на вопрос или подтверждения, перехват начнётся после того, как вы ответите. Завершённый запуск возвращает 409 с This run has finished.

Настройка тайм-аута#

human_timeout_minutes задаёт, сколько каждая пауза ждёт ответа: от 1 до 1440 минут, по умолчанию 30 (5 для запуска, начатого облачным агентом, за которым обычно никто не наблюдает). Когда сессия облачного агента заканчивается, его незавершённые запуски браузера останавливаются. Таймер перезапускается на каждой паузе. Запуск, превысивший тайм-аут, завершается статусом timed_out, и с вас списывается плата за его шаги и оплачиваемое время ожидания.

Более длинный тайм-аут даёт людям больше времени на ответ, но резервирует больше вашего баланса: резерв включает одно ожидание до тайм-аута по $0.005 в минуту. См. Как работает резерв.

Запуск без человека#

Для заданий без присмотра отключите паузы:

JSON
{
  "goal": "Get the current mortgage rates table.",
  "start_url": "https://bank.example.com/rates",
  "human": false,
  "approve": "none"
}
  • "human": false — агент никогда не задаёт вопросов и не передаёт управление. Если ему не хватает информации или он застрял, он продолжает как может, и запуск может завершиться статусом failed.
  • "approve": "none" — агент отправляет формы, не дожидаясь подтверждения.

Если заданы оба параметра, запуск ничего не резервирует на время ожидания.

Справочник по ответам#

POST /v1/browser/runs/{id}/reply принимает ровно одно из следующих тел:

ТелоДля needs.kindРезультат
{"answer": "…"}questionАгент читает ваш текст (до 2000 символов).
{"secret": "…"}questionАгент вводит значение на текущем сайте, не видя его (до 4096 символов).
{"approve": true}approvalАгент нажимает кнопку следующим шагом.
{"approve": false, "reason": "…"} или {"deny": true, "reason": "…"}approvalАгент ничего не отправляет и читает вашу причину.
{"done": true, "note": "…"}takeoverАгент продолжает с текущей страницы.
{"failed": true, "note": "…"}takeoverАгент продолжает, зная, что человек не смог закончить.

Он возвращает запуск, status которого снова running. Ошибки:

СтатусСообщениеПричина
400Reply with one of: …Тело не соответствует ни одной из форм выше.
404No such runНеверный id, или запуск принадлежит другому аккаунту.
409This run is not waiting for a replyЗапуск работает или уже завершён.
409This run is waiting for a reply of kind "question"Ответ не соответствует needs.kind (в сообщении указан ожидаемый вид).