{"protocolVersion":"0.3.0","name":"Human Browser","description":"Human Browser — a cloud browser agent. Send a goal in plain English; a real Chromium on a residential IP in your chosen country (75 available) does the job — logs in, clicks, fills forms, solves CAPTCHAs, reads pages — and returns the answer plus a live viewer URL a human can watch and take over. Logins persist per profile, so the second run on a site skips the login.\n\nUSE IT FOR: tasks a plain HTTP fetch cannot do — logged-in dashboards, sites behind Cloudflare/Turnstile/DataDome/PerimeterX, geo-restricted pages, multi-step forms, uploads. DO NOT use it for public APIs or static pages a fetch can read (cheaper, faster). Legitimate, user-authorized tasks only.\n\nTOKEN: Authorization: Bearer hb_live_... on every call (never in a URL). Get one at https://humanbrowser.cloud/account (Google or email sign-in). No free credit; the first top-up can be $1 (card), later top-ups from $5. Prepaid balance, no subscription, never expires.\n\nPRICE (list v2, since 2026-10-01; machine-readable https://humanbrowser.cloud/api/plans): $0.02 per agent step with the LLM included, $0.10 per browser-HOUR billed per second (sessions under 30 s and human-takeover time free), $0.005 per solved CAPTCHA, $4/GB residential traffic. Median successful task $0.24 (p25 $0.12, p75 $0.39), about 8 steps — $1 is roughly 4 tasks.\n\nRUN A TASK (A2A JSON-RPC 2.0, POST https://agent.humanbrowser.cloud/a2a):\n  {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"message/send\",\"params\":{\"message\":{\"role\":\"user\",\"messageId\":\"m1\",\"parts\":[{\"kind\":\"text\",\"text\":\"Open https://news.ycombinator.com and return the top 3 story titles as JSON.\"}]}}}\n1. message/send answers in seconds, once the browser is up: a Task {id:\"t_…\", status.state:\"working\", metadata.viewerUrl, metadata.sessionId, metadata.profile}. The run continues detached. Show metadata.viewerUrl to your user.\n2. Poll tasks/get {\"id\":\"t_…\"} every 3-5 s (no long-poll). working = still running, not a failure; status.message is a progress line (\"Step 12/100 on example.com — …\") you may quote verbatim. Or use message/stream (SSE), or set metadata.callback_url (https) to receive the final Task by POST.\n3. Terminal states: completed | failed | canceled | input-required. The answer is metadata.outcome.result (also artifacts[0]); metadata.recordUrl replays the run. Files the run downloaded come back as kind:file parts and metadata.outcome.produced_files.\n4. Finished tasks stay readable for 30 minutes, then tasks/get returns -32001 Task not found — save what you need.\nTypical duration: 20-90 s on a warm profile, 3-8 min for a first login on a hostile site. Hard limits: 2 h and 100 steps per task.\n\nCREDENTIALS — never in the goal text. Add a DataPart with FLAT string values and refer to each key as <key> in the goal:\n  {\"kind\":\"data\",\"metadata\":{\"sensitive\":true},\"data\":{\"email\":\"me@example.com\",\"password\":\"…\"}}  +  text \"Log into example.com as <email> with <password>, then …\"\nThe value is typed at action time and never reaches the model, logs or results. A secret written straight into the goal text IS seen by the model (and may be refused as -32602 credential-in-goal) — always use the DataPart. URLs are not substituted: write them plainly. Text that must be pasted exactly: <verbatim name=\"pitch\">…</verbatim>. Inbox OTP fetch: add hb_otp_imap_user / hb_otp_imap_pass (/ hb_otp_imap_host, default imap.gmail.com) to the same DataPart.\n\nINPUT-REQUIRED (the agent needs an OTP, a magic link, a decision): state=input-required, final; metadata.input_required = {req_id, question, kind, sensitive, timeout_s, deadline}. While it is still paused, answer with message/send: message.referenceTaskIds=[taskId], message.metadata.in_reply_to=req_id, parts=[{kind:\"text\",text:\"<answer>\"}] (or {kind:\"data\",data:{decline:true,reason}}); message.taskId=<taskId> or re-sending on the same contextId also works. The pause message carries a ready resume_example. A human can answer in the viewer instead. No answer by the deadline = auto-decline. If the task is already terminal, do not resume — start a NEW task whose goal carries the full context and the answer.\n\nFILES IN: attach {\"kind\":\"file\",\"file\":{\"name\":\"a.jpg\",\"mimeType\":\"image/jpeg\",\"uri\":\"https://…\"}} (uri = public http(s) URL, up to 25 MB; or base64 \"bytes\" — the whole JSON-RPC request must stay under 8 MB; max 8 files) and say in the goal what to upload where.\n\nOPTIONAL metadata (params.metadata or message.metadata): country (iso2, e.g. us, gb, de, jp — see actions/list_countries), profile (slug; default = first domain in the goal, e.g. adsy.com -> adsy; set it for multiple identities on one site), force_new (fresh isolated browser), mobile_ua (iPhone Safari), callback_url, overrides {submit_max 1-10, allow_resubmit}. Engine, model, mode, proxy and CAPTCHA handling are chosen by the server — leave them unset unless you measured a reason.\n\nSESSIONS: tasks on the same profile reuse the same live browser (still logged in) and queue behind each other (up to 20 waiting); different profiles run in parallel. Up to 10 concurrent sessions per token.\n\nERRORS: HTTP 401 / -32001 Unauthorized (bad token) · HTTP 402 / -32001 Payment Required {reason:\"balance-depleted\", topup_url} · -32602 Invalid params with detail \"goal_rejected (<reason>): …\" or \"credential-in-goal (…)\" (empty, junk, unfilled ${template}, or program code instead of an instruction — rewrite the goal; nothing billed) · task failed with metadata.error.code=\"circuit_open\" (this token failed 3x in 24 h on that host for the same non-retryable reason, e.g. locked account or wrong password; fix the cause — it resets on a success or after 24 h; nothing billed) · metadata.error.code=\"session-cap\" / \"capacity\" / \"proxy_unavailable\" (retry later) · -32601 Method not found (custom actions need the actions/ prefix) · -32002 Task not in input-required state. On failed, read metadata.outcome.result, metadata.outcome.blocks ({code, retryable, how_to_retry}) and metadata.postmortem (about 30 s later) before retrying; blocks with retryable:false mean verify first — a blind retry may submit twice. Report only what these fields say; do not guess \"blocked by a CAPTCHA\".\n\nACTIONS (same endpoint, method \"actions/<name>\"): list_countries, list_models, list_engines, switch_proxy_country, get_cost_snapshot, get_page_diagnostics (why a page looks broken: ok | degraded | page_did_not_start | broken_by_us), get_screenshots (frame links, not bytes), list_learned_apis + call_site_api (use a site's own API from your session), upload_file, set_profile_name, discover_endpoints. See customActions.\n\nMCP: the same agent at https://agent.humanbrowser.cloud/mcp (Streamable HTTP, Bearer token or OAuth 2.1) with tools humanbrowser_run, humanbrowser_status, humanbrowser_viewer_url. Full reference: https://humanbrowser.cloud/llms-full.txt","author":"Virix Labs","wellKnownURI":"https://agent.humanbrowser.cloud/.well-known/agent-card.json","url":"https://agent.humanbrowser.cloud/a2a","version":"5.2.0","provider":{"organization":"Virix Labs","url":"https://humanbrowser.cloud/"},"documentationUrl":"https://humanbrowser.cloud/docs","iconUrl":"https://humanbrowser.cloud/apple-touch-icon.png","supportsAuthenticatedExtendedCard":null,"security":[{"http_bearer":[]}],"securitySchemes":{"http_bearer":{"type":"http","scheme":"bearer","description":"API token from https://humanbrowser.cloud/account. Send as Authorization: Bearer <token> on every /a2a call. The agent card itself is public.","bearerFormat":"hb_live_<owner>_<32 hex>"}},"capabilities":{"streaming":true,"pushNotifications":false,"stateTransitionHistory":false,"extensions":[{"uri":"https://humanbrowser.cloud/a2a-ext/input-required/v1","params":{"ask_schema":{"kind":"text|url|email|otp|password|approval","choice":"yes_no | null — when set, accepted answers are 'yes'/'no'","req_id":"string, matches ^req_[a-zA-Z0-9_]{4,32}$","summary":"string<=500, optional context blurb","deadline":"epoch_ms","sensitive":"boolean — hide echo; never log","timeout_s":"integer 5..1800, server-side auto-decline after deadline"},"stream_resume":"message/stream with the same envelope re-opens an SSE stream on the same task","after_terminal":"An answer only works while state is input-required. After the deadline the ask auto-declines; once the task is terminal, start a new task with the full context plus the answer in the goal.","viewer_fallback":"A human can also answer from the live viewer at metadata.viewerUrl — first writer wins","resume_protocols":[{"name":"humanbrowser_v1","note":"preferred; resume_example in the pause DataPart is a ready envelope","shape":"message.referenceTaskIds=[<paused_task_id>], message.metadata.in_reply_to=<req_id>, parts=[TextPart(answer)] OR parts=[DataPart({decline:true,reason})]","method":"message/send"},{"name":"spec_task_id","shape":"message.taskId=<paused_task_id>, parts=[TextPart(answer)] OR parts=[DataPart({decline:true,reason})]","method":"message/send"},{"name":"same_context","shape":"message.contextId=<original contextId>, parts=[TextPart(answer)] — attached to the paused task of that context (same token only)","method":"message/send"}]},"required":false,"description":"Agent pauses with state=input-required (final:true on the SSE stream) when it needs an OTP, magic link, credential or decision it cannot derive. The pause message carries a TextPart (the question) and a DataPart {req_id, kind, sensitive, timeout_s, deadline, choice, summary, resume_hint, resume_example}; the same fields are mirrored on tasks/get as metadata.input_required."},{"uri":"https://humanbrowser.cloud/a2a-ext/relay/v1","params":{"activation":"metadata.engine=\"relay\" on message/send","fallback_behavior":"no recipe -> task fails quickly; retry without the engine override"},"required":false,"description":"Experimental reverse-API relay: for a small set of mapped domains, metadata.engine=\"relay\" calls the site's own JSON endpoints instead of driving a browser. There is no public recipe catalog endpoint; if the domain has no recipe the task fails fast — run it again without engine."},{"uri":"https://humanbrowser.cloud/a2a-ext/engine-router/v1","params":{"modes":["fast","stealth"],"engines":["patchright","cloak","cua","adspower","remote-cdp","relay"],"list_actions":["actions/list_engines","actions/list_models","actions/list_remote_providers"],"override_metadata_keys":["engine","mode","model"]},"required":false,"description":"Per-domain engine + model auto-router. The server picks the execution engine and LLM per goal from site rules and past runs. Override with metadata.engine / metadata.model only with a measured reason. The live catalog is actions/list_engines and actions/list_models."},{"uri":"https://humanbrowser.cloud/a2a-ext/viewer/v1","params":{"auth":"the ?k= key grants access to that session (and may show typed data on screen) — share it only with people who should see the session","url_pattern":"https://humanbrowser.cloud/a/s_<sessionId>?k=<key>","capabilities":["live_stream","input_takeover","input_required_modal","replay"],"delivered_in":"metadata.viewerUrl on every message/send response and tasks/get; the first status message also contains it"},"required":false,"description":"Live viewer with human takeover. Every session has a viewer URL https://humanbrowser.cloud/a/s_<sessionId>?k=<key> (metadata.viewerUrl). A human can watch, click and type in it, and answer input-required prompts. Takeover time is not billed. After the run, metadata.recordUrl (viewerUrl + &replay=1) replays it."},{"uri":"https://humanbrowser.cloud/a2a-ext/errors/v1","params":{"http_level":{"401":"missing/unknown/revoked token (JSON-RPC -32001)","402":"balance depleted (JSON-RPC -32001, data.topup_url)"},"circuit_open":"same token + same host failed 3 times in 24 h with the same non-retryable class (account locked, wrong credentials, ...). Further runs on that host are refused before launch until a success or the failures age past 24 h. Fix the cause; retrying changes nothing.","spawn_failures":"session-cap (too many concurrent sessions for this token), capacity / proxy_unavailable (server busy — retry in ~60-90 s): returned as a failed Task whose status message names the code","metadata_error_shape":{"code":"circuit_open | goal_rejected | credential-in-goal | unsupported_flow | unsupported_request | queue-full | ...","host":"circuit_open only","class":"circuit_open only","billed":"false when nothing was charged","message":"string","failures":"circuit_open only","resets_at":"circuit_open only (ISO time)","http_status":"int"},"goal_rejected_reasons":["empty_goal","no_instruction","junk_goal","unfilled_template","goal_is_code"]},"required":false,"description":"Machine-readable refusals. Goal/credential problems come back as JSON-RPC -32602 (detail starts with \"credential-in-goal\" or \"goal_rejected (<reason>)\"); refusals after the browser was requested come back as a failed Task with metadata.error."}]},"defaultInputModes":["text/plain","application/json","image/png","image/jpeg","image/webp","image/gif","application/pdf","video/mp4","application/octet-stream"],"defaultOutputModes":["text/plain","application/json","application/octet-stream"],"skills":[{"id":"browser_task","name":"Browser Task","description":"Free-form web task in plain language: navigate, search, click, fill forms, read and extract. Returns the answer as text (metadata.outcome.result, artifacts[0]) plus a live viewer URL (metadata.viewerUrl) the end user can watch or take over.","tags":["browser","automation","scraping","navigation","stealth"],"examples":["Open https://news.ycombinator.com and return the top 3 story titles as JSON.","On skyscanner.com find the cheapest one-way flight Bangkok -> Tokyo next Friday and return airline, times and price.","Open polymarket.com and report the current odds on the top US politics market."],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"login_and_scrape","name":"Login and Scrape","description":"Log into a site with credentials supplied in a sensitive DataPart, then extract data from the logged-in pages. DataPart: {\"kind\":\"data\",\"metadata\":{\"sensitive\":true},\"data\":{\"email\":\"...\",\"password\":\"...\"}} (flat strings); goal refers to <email> and <password>. Values are typed at action time and never echoed. Cookies persist on the profile, so later tasks on the same site skip the login. If the site asks for a code the agent pauses with input-required; add hb_otp_imap_user / hb_otp_imap_pass to the DataPart and it reads the code from the inbox itself.","tags":["browser","login","scraping","authenticated"],"examples":["Log into reddit.com as <email> with <password> and list my 10 newest notifications.","Log into app.example.com with <email> / <password> and return the last 5 invoices as JSON {number, date, amount}."],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"meta_business_workflow","name":"Meta Business Workflow","description":"Drive Meta Business Suite / Facebook Ads Manager / Meta-owned web properties on behalf of a specific Meta account, using customer-supplied cookies+proxy+UA bundled into a persistent AdsPower-backed Chromium profile. Use this when the goal requires touching an authenticated Meta surface (pulling campaign performance, dispatching ad creation, reading Page inbox, exporting audience reports) and the end-user has a Meta account they already operate manually. Pass metadata.engine='adspower' and metadata.profile=<account_slug> on message/send, plus the account bundle (cookies, user agent, proxy) in a DataPart with metadata.sensitive=true so the same fingerprint is reused across follow-up tasks. Surcharge: +$0.05/session. Login flows on Meta routinely trip 2FA / checkpoint / device-trust prompts, so relaying the response viewer URL to the end user is typically helpful. Since 2026-09-07: Meta's div[role=button] controls that ignored synthetic clicks (Create a business portfolio, Save changes, Post, I accept) are clicked with a real OS mouse automatically when the first click changes nothing; avatar/cover uploads take an attached FilePart or a public URL; cover repositioning uses a real drag. A named profile (metadata.profile) keeps the Facebook login between tasks — do not pass force_new for follow-up work on the same account.","tags":["browser","meta","facebook","ads-manager","business-suite","multi-account","adspower"],"examples":["Open Meta Ads Manager for the supplied account and report last-7-day spend + CPM per active campaign","In Meta Business Suite, read unread Page inbox messages for the supplied account and summarise them","Duplicate the top-performing ad set in campaign <id> with budget +20%, paused"],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"fill_form","name":"Fill Form","description":"Open a URL, fill a form from structured data, submit, and report the confirmation. Put non-secret field values in a plain DataPart (they become context for the agent) and secrets in a sensitive one. A submit guard stops repeated clicks on submit/pay buttons; if metadata.outcome.blocks shows submit_guard (retryable:false), check whether the submission landed before re-running with metadata.overrides.","tags":["browser","form","submit"],"examples":["Open https://example.com/contact and submit the form with the values in the data part, then return the confirmation text."],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"scrape_url","name":"Scrape URL","description":"URL in, structured data out: \"Extract name, price and availability from <url> as JSON\". The page is rendered in real Chromium (JavaScript, cookies, residential IP), so it works on SPAs, logged-in pages and geo-restricted views. The JSON comes back as text in metadata.outcome.result. For bulk static pages a plain HTTP scraper is cheaper.","tags":["browser","scraping","url-to-json","structured-extraction"],"examples":["Scrape https://example.com/product/123 and return {name, price, in_stock, rating}","Extract the article title, author, date, and body text from https://blog.example.com/post-slug","Return the top-10 job listings visible on https://careers.example.com/search?q=engineer as a JSON array"],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"relay_reverse_api","name":"Relay (reverse-API)","description":"Experimental: for a few mapped domains (property portals, hotel aggregators), metadata.engine=\"relay\" calls the site's own JSON endpoints instead of driving a browser — faster and cheaper. There is no public catalog of mapped domains; if a domain is not mapped the task fails fast and should be re-run without engine.","tags":["reverse-api","relay","fast-path","no-browser","cost-optimized"],"examples":["engine=relay: List 20 rentals on Renthub for Sukhumvit district under 30000 THB/month","engine=relay: Fetch DotProperty listing details for URL https://dotproperty.co.th/en/property/123456","engine=relay: Return current price for Agoda cityId 15843 checkin 2026-08-01 checkout 2026-08-03 room=1 pax=2"],"inputModes":["text/plain","application/json"],"outputModes":["application/json"]},{"id":"hostile_site_solver","name":"Hostile Site Solver","description":"Sites behind interactive anti-bot walls (Cloudflare Managed Challenge / Turnstile, DataDome, PerimeterX press-and-hold, Akamai). Leave engine unset — the router picks the path that works for that vendor (stealth Chromium, remote browser or OS-level computer-use driving). CAPTCHA solves are $0.005 each, only when one is solved. If a page still looks broken, call actions/get_page_diagnostics before concluding the site blocked you.","tags":["anti-bot","cloudflare","turnstile","perimeterx","datadome","cua","hard-target"],"examples":["Search for 'senior software engineer' on linkedin.com/jobs (US) and return top 10 results (hostile: LinkedIn bot detection)","Fetch product listing from etsy.com/shop/StoreName sorted by newest — full titles, prices, thumbnails (hostile: PerimeterX press-and-hold)","Open realtor.com and return top-20 listings for zip 90210 sorted by price (hostile: DataDome managed challenge)"],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"email_verified_signup","name":"Email-Verified Signup","description":"Sign up on sites that need Turnstile + email verification. Sensitive DataPart (flat strings): {\"email\":\"...\",\"password\":\"...\",\"hb_otp_imap_user\":\"...\",\"hb_otp_imap_pass\":\"...\",\"hb_otp_imap_host\":\"imap.gmail.com\"} — the agent registers, solves the challenge, reads the verification code or magic link from that inbox over IMAP, completes verification and returns what you asked for (API key, dashboard URL). Use a mailbox you control; use an app password for Gmail.","tags":["signup","onboarding","email-otp","turnstile","hunter","apollo","zerobounce"],"examples":["Sign up to hunter.io with <email> / <password>, verify the email, and return the API key from the dashboard.","Register on snov.io with <email> / <password>, confirm the email and return the free-tier API token."],"inputModes":["text/plain","application/json"],"outputModes":["text/plain","application/json"]},{"id":"network_discovery","name":"Network endpoint discovery","description":"Capture a page's own XHR/fetch traffic from a live session and return a de-duplicated list of its API endpoints with freshness signals. No LLM, no per-step charge; cookie/authorization headers are stripped. Call actions/discover_endpoints {taskId (or sessionId+sessionKey), url, country?, duration_ms<=90000, scroll?, reload?, match_url?, freshness_fields?} on a session you already started, then poll actions/get_discovery_result {job_id}.","tags":["browser","network","reverse-api","discovery","api-scout"],"examples":["actions/discover_endpoints {\"taskId\":\"t_…\",\"url\":\"https://www.example-marketplace.com/search?q=bike\"}"],"inputModes":["text/plain","application/json"],"outputModes":["application/json","text/plain"]}],"conformance":true,"conformance_errors":null,"homepage":null,"repository":null,"license":null,"pricing":null,"contact":null,"id":"d238edb6-2cfa-441a-852c-8c8d3cbe166c","created_at":"2026-05-23T18:43:39.462025Z","updated_at":"2026-10-12T23:41:55.258039Z","hidden":false,"flag_count":0,"maintainer_notes":"Agent endpoint returns **401 Unauthorized**. Callers need credentials, but the agent card should declare `securitySchemes` and `security` so clients know how to authenticate. See the A2A spec.","category":"agent-orchestration","category_secondary":null,"uptime_percentage":100.0,"avg_response_time_ms":1546,"last_health_check":"2026-10-12T23:41:55.157311Z","is_healthy":true,"status_notes":["Returning errors when contacted by users (Agent returned an unexpected error)"],"task_conformance":{"category":"401","passed":false,"checked_at":"2026-10-12T07:07:45.149382Z","response_ms":791}}