@@ -210,6 +210,8 @@ class SolveRequest(BaseModel):
210210 concurrency : Optional [int ] = Field (
211211 None , ge = 0 , le = 64 , description = "Turnstile-only maximum simultaneous browser attempts. "
212212 "Use 0 for unlimited; otherwise overrides TURNSTILE_CONCURRENCY." )
213+ cookies : Optional [list [dict [str , Any ]]] = Field (
214+ None , description = "Browser cookies to restore before real-page navigation." )
213215
214216 # reCAPTCHA-only
215217 version : Optional [str ] = Field (None , description = "reCAPTCHA only: v2 | v3 | invisible (default v2)." )
@@ -232,6 +234,41 @@ class SolveRequest(BaseModel):
232234 render_flow : Optional [str ] = Field (None , description = "perimeterx: named site trigger that makes the gate render when it doesn't show on plain load (default 'outlook_signup'). Throwaway navigation only — NOT account creation. Pass null with a `url` for deployments whose gate renders on goto(). Harvests the _px3 clearance cookie (bound to _pxvid+IP+UA; replay under the same proxy+UA within TTL)." )
233235
234236
237+ class XaiSessionStartRequest (BaseModel ):
238+ signup_url : str = "https://accounts.x.ai/sign-up?redirect=grok-com"
239+ proxy : Optional [str ] = None
240+ timeout_s : int = Field (120 , ge = 15 , le = 180 )
241+
242+
243+ class XaiSessionEmailRequest (BaseModel ):
244+ session_id : str
245+ email : str
246+
247+
248+ class XaiSessionVerifyRequest (XaiSessionEmailRequest ):
249+ code : str
250+
251+
252+ class XaiSessionSignupRequest (XaiSessionVerifyRequest ):
253+ password : str
254+ given_name : str
255+ family_name : str
256+ turnstile_token : str
257+ action_id : str
258+ next_router_state_tree : str
259+
260+
261+ class XaiSessionAuthorizeRequest (BaseModel ):
262+ session_id : str
263+ verification_url : str
264+ user_code : str
265+ timeout_s : int = Field (120 , ge = 15 , le = 600 )
266+
267+
268+ class XaiSessionCloseRequest (BaseModel ):
269+ session_id : str
270+
271+
235272# Named request examples → Swagger UI renders these as a dropdown picker on /solve.
236273_SOLVE_EXAMPLES = {
237274 "turnstile" : {
@@ -344,6 +381,102 @@ async def health():
344381 return {"status" : "ok" , "supported_types" : SUPPORTED }
345382
346383
384+ def _xai_http_error (error : Exception ) -> HTTPException :
385+ from xai_browser .flow import XaiBrowserFlowError
386+
387+ if isinstance (error , XaiBrowserFlowError ):
388+ status = 409 if error .stage in {"browser_session" , "cookie_setter" , "approve" } else 400
389+ if error .retryable :
390+ status = 503
391+ return HTTPException (status , detail = str (error ))
392+ return HTTPException (500 , detail = f"xAI browser flow failed: { type (error ).__name__ } " )
393+
394+
395+ @app .post ("/xai/session/start" , dependencies = [Depends (_bearer )], tags = ["solve" ])
396+ async def xai_session_start (req : XaiSessionStartRequest ):
397+ from xai_browser .flow import start_session
398+
399+ _assert_public_url (req .signup_url , "signup_url" )
400+ try :
401+ return await start_session (
402+ signup_url = req .signup_url ,
403+ proxy = req .proxy or "" ,
404+ timeout_s = req .timeout_s ,
405+ )
406+ except Exception as error :
407+ raise _xai_http_error (error ) from error
408+
409+
410+ @app .post ("/xai/session/send-email" , dependencies = [Depends (_bearer )], tags = ["solve" ])
411+ async def xai_session_send_email (req : XaiSessionEmailRequest ):
412+ from xai_browser .flow import send_email
413+
414+ try :
415+ return await send_email (req .session_id , req .email )
416+ except Exception as error :
417+ raise _xai_http_error (error ) from error
418+
419+
420+ @app .post ("/xai/session/verify-email" , dependencies = [Depends (_bearer )], tags = ["solve" ])
421+ async def xai_session_verify_email (req : XaiSessionVerifyRequest ):
422+ from xai_browser .flow import verify_email
423+
424+ try :
425+ return await verify_email (req .session_id , req .email , req .code )
426+ except Exception as error :
427+ raise _xai_http_error (error ) from error
428+
429+
430+ @app .post ("/xai/session/signup" , dependencies = [Depends (_bearer )], tags = ["solve" ])
431+ async def xai_session_signup (req : XaiSessionSignupRequest ):
432+ from xai_browser .flow import signup
433+
434+ try :
435+ return await signup (
436+ req .session_id ,
437+ email = req .email ,
438+ password = req .password ,
439+ code = req .code ,
440+ given_name = req .given_name ,
441+ family_name = req .family_name ,
442+ turnstile_token = req .turnstile_token ,
443+ action_id = req .action_id ,
444+ next_router_state_tree = req .next_router_state_tree ,
445+ )
446+ except Exception as error :
447+ raise _xai_http_error (error ) from error
448+
449+
450+ @app .post ("/xai/session/authorize-device" , dependencies = [Depends (_bearer )], tags = ["solve" ])
451+ async def xai_session_authorize_device (req : XaiSessionAuthorizeRequest ):
452+ from xai_browser .flow import authorize_device
453+
454+ _assert_public_url (req .verification_url , "verification_url" )
455+ try :
456+ return await authorize_device (
457+ req .session_id ,
458+ verification_url = req .verification_url ,
459+ user_code = req .user_code ,
460+ timeout_s = req .timeout_s ,
461+ )
462+ except Exception as error :
463+ raise _xai_http_error (error ) from error
464+
465+
466+ @app .post ("/xai/session/close" , dependencies = [Depends (_bearer )], tags = ["solve" ])
467+ async def xai_session_close (req : XaiSessionCloseRequest ):
468+ from xai_browser .flow import close_session
469+
470+ return {"ok" : True , "closed" : await close_session (req .session_id )}
471+
472+
473+ @app .on_event ("shutdown" )
474+ async def close_xai_browser_sessions ():
475+ from xai_browser .flow import close_all_sessions
476+
477+ await close_all_sessions ()
478+
479+
347480def _extract (req : SolveRequest ):
348481 """Unpack pre_actions + post_fetch for realpage endpoints."""
349482 actions = [a .model_dump () for a in req .pre_actions ] if req .pre_actions else None
@@ -371,7 +504,8 @@ async def _dispatch(req: SolveRequest) -> dict:
371504 actions , fetches = _extract (req )
372505 r = await solve_turnstile_realpage (
373506 req .url , req .sitekey , req .timeout_s , actions , fetches , req .proxy ,
374- req .action , req .cdata , req .concurrency , req .queue_timeout_s )
507+ req .action , req .cdata , req .concurrency , req .queue_timeout_s ,
508+ req .cookies )
375509 else :
376510 r = await solve_turnstile (
377511 req .sitekey , req .url , req .action , req .cdata , req .proxy ,
0 commit comments