
Lucas Mitchell
Automation Engineer

FunctionTool; do not expose API keys, proxy credentials, cookies, or raw browser objects to the model.websiteURL, websiteKey, and pageAction from the live authorized workflow. Never let the agent invent them.ReCaptchaV3TaskProxyLess for server-proxy token mode or ReCaptchaV3Task when an approved proxy must be supplied.A reliable LlamaIndex reCAPTCHA v3 solver is a typed recovery tool, not an open-ended browsing capability. The LlamaIndex agent should decide when an approved task is blocked, while trusted code validates the target, reads the exact site key and action from the current page, calls CapSolver, submits the token, and verifies the expected state. This separation matters because reCAPTCHA v3 runs without an interactive checkbox and evaluates an action-specific request. A token created for the wrong URL or pageAction can be rejected even when the API call itself succeeds. This guide shows the official CapSolver task fields, an async LlamaIndex FunctionTool, server-side policy controls, session-mode handling, structured results, bounded retries, browser verification, and production observability.
LlamaIndex's official tools documentation explains that FunctionTool wraps synchronous or asynchronous Python functions and can infer a function schema. It also notes that tool names, descriptions, and argument descriptions strongly influence how a model selects and calls a tool.
For a LlamaIndex reCAPTCHA v3 solver, keep the tool narrow:
LlamaIndex agent
↓ chooses a typed tool
FunctionTool wrapper
↓ validates trusted references
CapSolver executor
↓ returns a short-lived solution
Browser service
↓ submits and verifies
LlamaIndex workflow resumes
The CapSolver AI Agents documentation describes the same division of labor: the model decides, the adapter exposes schemas, and the core executes supported challenge work.
CapSolver's reCAPTCHA v3 documentation defines four task types:
| Task type | Proxy mode | Enterprise |
|---|---|---|
ReCaptchaV3TaskProxyLess |
CapSolver server proxy | No |
ReCaptchaV3Task |
Your approved proxy | No |
ReCaptchaV3EnterpriseTaskProxyLess |
CapSolver server proxy | Yes |
ReCaptchaV3EnterpriseTask |
Your approved proxy | Yes |
The base fields are:
| Field | Requirement | Trusted source |
|---|---|---|
websiteURL |
Required | Current authorized page URL |
websiteKey |
Required | Live page configuration |
pageAction |
Usually required for v3 | The page's grecaptcha.execute action |
proxy |
Required for non-proxyless task | Server-side approved proxy profile |
enterprisePayload |
Conditional | Live Enterprise configuration |
isSession |
Conditional | Target-specific approved workflow |
Google's reCAPTCHA v3 guide describes action names as part of the integration. The action observed on the page should be preserved exactly.
The CapSolver reCAPTCHA blog contains additional troubleshooting and implementation guides.
Pass references to server-side state, not arbitrary values.
from dataclasses import dataclass
from urllib.parse import urlparse
@dataclass(frozen=True)
class CaptchaContext:
context_id: str
website_url: str
website_key: str
page_action: str
enterprise: bool = False
proxy_profile: str | None = None
session_mode: bool = False
TRUSTED_CONTEXTS: dict[str, CaptchaContext] = {}
ALLOWED_HOSTS = {"staging.example.com", "portal.example.org"}
def get_trusted_context(context_id: str) -> CaptchaContext:
context = TRUSTED_CONTEXTS.get(context_id)
if context is None:
raise ValueError("Unknown CAPTCHA context")
host = urlparse(context.website_url).hostname
if host not in ALLOWED_HOSTS:
raise PermissionError("Target is outside the approved host policy")
if not context.website_key or not context.page_action:
raise ValueError("Trusted context is missing required v3 parameters")
return context
The model receives only context_id. The browser service owns the current page, site key, action, and proxy binding.
The user-provided CapSolver Agent documentation specifies installing the core package before the agent package:
pip install git+https://github.com/capsolver-ai/capsolver-core.git
pip install git+https://github.com/capsolver-ai/capsolver-agent.git
pip install llama-index-core
Set the API key in the runtime environment:
export CAPSOLVER_API_KEY="your-capsolver-api-key"
Do not paste the key into prompts, notebooks, scenario datasets, or traces. The CapSolver AI and automation FAQ explains the integration model.
capsolver-agent provides create_executor() for the model–adapter–core boundary.
import os
from capsolver_agent.schema import create_executor
executor = create_executor(
api_key=os.environ["CAPSOLVER_API_KEY"],
default_timeout=120,
)
The executor dispatches solve_captcha to CapSolver Core and returns a structured result. Keep it in trusted application code.
The function resolves the trusted context, selects the official task type, and invokes the executor.
from typing import Annotated
async def solve_recaptcha_v3(
context_id: Annotated[
str,
"Opaque ID for a trusted, current browser CAPTCHA context"
],
) -> dict:
"""Solve reCAPTCHA v3 for an approved browser context.
Use only when the current workflow reports a supported reCAPTCHA v3
checkpoint. Never guess or modify the target URL, site key, or action.
"""
context = get_trusted_context(context_id)
captcha_type = (
"reCaptchaV3Enterprise"
if context.enterprise
else "reCaptchaV3"
)
args = {
"captcha_type": captcha_type,
"website_url": context.website_url,
"website_key": context.website_key,
"page_action": context.page_action,
}
if context.proxy_profile:
args["proxy"] = resolve_proxy(context.proxy_profile)
result = await executor.execute("solve_captcha", args)
if not result.get("success"):
return {
"success": False,
"context_id": context_id,
"error": normalize_error(result.get("error")),
}
solution = result.get("solution") or {}
token = solution.get("token")
if not token:
return {
"success": False,
"context_id": context_id,
"error": "solution did not contain a token",
}
receipt = await submit_solution_and_verify(
context_id=context_id,
token=token,
session_cookie=extract_session_cookie(solution),
)
return {
"success": receipt["verified"],
"context_id": context_id,
"verified": receipt["verified"],
"next_state": receipt["next_state"],
}
resolve_proxy, normalize_error, and submit_solution_and_verify are application-owned policy adapters. They should not be visible to the model.
from llama_index.core.tools import FunctionTool
tool = FunctionTool.from_defaults(
async_fn=solve_recaptcha_v3,
name="solve_recaptcha_v3",
description=(
"Solve reCAPTCHA v3 for an approved current browser context. "
"Input must be an opaque context_id supplied by the browser service. "
"Do not call for unsupported pages or unapproved hosts."
),
)
Inspect the schema during development:
schema = tool.metadata.get_parameters_dict()
print(schema)
This follows LlamaIndex's documented FunctionTool pattern while reducing the model's argument surface to one opaque identifier.
from llama_index.core.agent.workflow import FunctionAgent
agent = FunctionAgent(
llm=llm,
tools=[tool],
system_prompt=(
"Operate only approved browser workflows. When the browser service "
"reports a supported reCAPTCHA v3 checkpoint, call "
"solve_recaptcha_v3 with the supplied context_id. Call once. "
"Continue only when verified=true; otherwise request review."
),
)
Run the workflow with a trusted browser observation:
response = await agent.run(
"The approved staging workflow is waiting at a reCAPTCHA v3 "
"checkpoint. Use context_id ctx_7f19 and continue only if verified."
)
The agent never sees the API key, raw proxy, token, or cookie.
pageAction from the Live PageA reliable LlamaIndex reCAPTCHA v3 solver should not reuse a generic action such as login across every target. The browser service should read the target's current integration.
async def collect_v3_context(page, context_id: str) -> CaptchaContext:
website_url = page.url
host = urlparse(website_url).hostname
if host not in ALLOWED_HOSTS:
raise PermissionError("Unapproved target")
values = await page.evaluate("""
() => {
const scripts = Array.from(document.scripts)
.map(s => s.textContent || '')
.join('\n');
const siteKey =
document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')
|| null;
const actionMatch = scripts.match(
/grecaptcha(?:\.enterprise)?\.execute\([^,]+,\s*\{\s*action:\s*['\"]([^'\"]+)/
);
return {
siteKey,
pageAction: actionMatch ? actionMatch[1] : null,
enterprise: scripts.includes('grecaptcha.enterprise')
};
}
""")
if not values["siteKey"] or not values["pageAction"]:
raise RuntimeError("Could not read required v3 parameters")
return CaptchaContext(
context_id=context_id,
website_url=website_url,
website_key=values["siteKey"],
page_action=values["pageAction"],
enterprise=values["enterprise"],
)
For complex integrations, use the CapSolver extension guide to inspect page parameters during approved development and testing.
CapSolver's official v3 documentation notes that some targets can return recaptcha-ca-t when isSession is enabled. Treat it as sensitive, short-lived session material.
SESSION_KEYS = {
"recaptcha-ca-t",
"recaptcha_ca_t",
}
def extract_session_cookie(solution: dict) -> str | None:
raw = solution.get("raw") or {}
for key in SESSION_KEYS:
value = solution.get(key) or raw.get(key)
if value:
return value
return None
Only enable session mode when the target integration requires it and the workflow is authorized. Store the value in process memory or short-lived encrypted storage; never place it in the LlamaIndex context.
Google's server-side verification documentation explains that a site validates the token on its backend. Your automation should submit the token through the same approved application flow, then verify the resulting page state.
async def submit_solution_and_verify(
context_id: str,
token: str,
session_cookie: str | None,
) -> dict:
browser_state = BROWSER_CONTEXTS[context_id]
page = browser_state.page
if session_cookie:
await browser_state.context.add_cookies([{
"name": "recaptcha-ca-t",
"value": session_cookie,
"domain": urlparse(page.url).hostname,
"path": "/",
"secure": True,
}])
await page.evaluate(
"""({ token }) => {
let input = document.querySelector(
'textarea[name="g-recaptcha-response"]'
);
if (!input) {
input = document.createElement('textarea');
input.name = 'g-recaptcha-response';
input.style.display = 'none';
document.body.appendChild(input);
}
input.value = token;
input.dispatchEvent(new Event('change', { bubbles: true }));
}""",
{"token": token},
)
await trigger_trusted_callback(page, browser_state.callback_name)
try:
await page.locator(browser_state.success_selector).wait_for(
state="visible",
timeout=15000,
)
return {"verified": True, "next_state": "continue"}
except Exception:
return {"verified": False, "next_state": "operator_review"}
Callback discovery is target-specific. Capture it in the trusted browser context rather than asking the model to generate JavaScript.
The CapSolver reCAPTCHA response API guide explains common response handling patterns.
from enum import Enum
class RecoveryState(str, Enum):
DETECTED = "detected"
SOLVING = "solving"
VERIFIED = "verified"
REVIEW_REQUIRED = "review_required"
ATTEMPTS: dict[str, int] = {}
async def guarded_solve(context_id: str) -> dict:
attempts = ATTEMPTS.get(context_id, 0)
if attempts >= 1:
return {
"success": False,
"context_id": context_id,
"next_state": RecoveryState.REVIEW_REQUIRED,
"error": "recovery budget exhausted",
}
ATTEMPTS[context_id] = attempts + 1
return await solve_recaptcha_v3(context_id)
A repeated call often signals stale parameters, wrong action, expired browser state, or an unsupported path. Stop the loop and collect diagnostics.
Log operational metadata, not secrets.
from datetime import datetime, timezone
def recovery_event(context: CaptchaContext, result: dict) -> dict:
return {
"event": "recaptcha_v3_recovery",
"context_id": context.context_id,
"host": urlparse(context.website_url).hostname,
"page_action": context.page_action,
"enterprise": context.enterprise,
"session_mode": context.session_mode,
"success": result.get("success", False),
"next_state": str(result.get("next_state")),
"observed_at": datetime.now(timezone.utc).isoformat(),
}
Do not log websiteKey if your policy treats it as configuration, and never log solution tokens, session cookies, API keys, raw proxies, or full private page HTML.
The CapSolver errors FAQ can help normalize error categories.
Bonus Code: Use code WEBS at CapSolver Dashboard to get an extra 5% bonus on every recharge.
| Integration pattern | Model input | Secret exposure risk | Best use |
|---|---|---|---|
| Model supplies all task fields | URL, key, action, proxy | High | Avoid in production |
| Typed FunctionTool with validated fields | Explicit fields | Medium | Controlled prototypes |
| Opaque context ID plus server validation | Context reference only | Low | Production LlamaIndex workflows |
Browser-only core solve_on_page |
No model parameters | Lowest | Deterministic Playwright recovery |
The opaque-context pattern gives the LlamaIndex agent enough control to request recovery without letting it rewrite sensitive or target-specific parameters.
websiteKey and pageAction from the current live page.The CapSolver products page lists supported solution categories, while the CapSolver AI blog covers related agent integration patterns.
Use this workflow only on applications you own, test, or have explicit permission to automate. Technical capability does not grant access rights. Respect target terms, rate limits, privacy requirements, and authentication boundaries. Do not use an agent tool to access private accounts, restricted records, or third-party workflows without authorization. Keep high-impact actions such as submission, payment, booking, and account changes behind a separate policy and confirmation step.
A production LlamaIndex reCAPTCHA v3 solver should expose one narrow, typed recovery function. The browser service supplies a trusted context ID, server-side code preserves the exact URL, site key, action, Enterprise mode, and proxy policy, CapSolver returns a short-lived solution, and the browser verifies the expected state before the agent continues.
Start an approved LlamaIndex integration with CapSolver, test it on a controlled staging workflow, and add parameter-grounding and retry assertions before production.
No. reCAPTCHA v3 is score-based and usually runs in the background. The workflow must preserve the target's site key, URL, and action.
pageAction important?The action identifies the operation being evaluated, such as login or submit. Use the exact action read from the live integration rather than a generic value.
Prefer server-side submission and return only a verified status. A token is short-lived runtime data and should not enter model context or logs.
Enable it only when the authorized target requires the returned session value. Keep that value in short-lived encrypted runtime storage.
Stop after the configured attempt budget, record a redacted diagnostic event, refresh trusted page parameters if appropriate, and route the workflow to operator review.
CloakBrowser review for 2026 covering features, pricing, licensing, deployment options, advantages, limitations, alternatives, and ideal use cases.

Build a CrewAI reCAPTCHA v3 solver with CapSolver, typed tools, trusted page actions, server-side token submission, score policy, and page verification.
