Skip to main content
When you enable CAPTCHA solving in your project, CAPTCHAs are solved automatically in the background. However, your automation may need to wait for a CAPTCHA to be solved before proceeding, or know when a CAPTCHA was solved. These helpers let you wait for CAPTCHAs to be solved and react to status changes.
CAPTCHA is a general term for challenges that verify you’re human. This includes reCAPTCHA, hCaptcha, Cloudflare Turnstile, and similar services.

Available helpers

Function reference

waitForCaptchaSolve

import { waitForCaptchaSolve } from '@intuned/runtime';
import type { Page } from 'playwright';

export declare function waitForCaptchaSolve(
  page: Page,
  options?: {
    timeoutInMs?: number;
    settleDurationMs?: number;
  }
): Promise<void>;
Wait for a CAPTCHA to be solved. Listens for CAPTCHA updates and resolves when all CAPTCHAs are solved or operation times out. Used when a CAPTCHA appears and you need to block until it’s solved.
Parameters
page
Page
required
Playwright page object.
options
object
Configuration options for the wait behavior.
options.timeoutInMs
number
default:"10000"
Maximum wait time in milliseconds. Throws TimeoutError if exceeded.
options.settleDurationMs
number
default:"5000"
Wait time in milliseconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.

Returns Returns Promise<void> when solved or settle period elapses.
Raises
  • TimeoutError — Thrown when timeoutInMs elapses while CAPTCHAs are still being solved.
  • CaptchaSolveError — Thrown when the CAPTCHA solver fails. Contains a CaptchaError with the error code and details.
  • Error — Thrown when listeners cannot be attached or the page context is invalid.

withWaitForCaptchaSolve

import { withWaitForCaptchaSolve } from '@intuned/runtime';
import type { Page } from 'playwright';

export declare function withWaitForCaptchaSolve<T>(
  callback: (page: Page) => Promise<T>,
  options: {
    page: Page;
    timeoutInMs?: number;
    settleDurationMs?: number;
    waitForNetworkSettled?: boolean;
  }
): Promise<T>;
Execute a callback then wait for CAPTCHA to be solved. Execute the provided callback function, then listen for CAPTCHA updates and resolve when all CAPTCHAs are solved or operation times out. Useful when you need to execute a method that triggers a CAPTCHA and must block until it’s solved.
Parameters
callback
(page: Page) => Promise<T>
required
Function to execute before waiting for solve. Receives the page object and can return a value.
options
object
required
Configuration options for the wait behavior.
options.page
Page
required
Playwright page object.
options.timeoutInMs
number
default:"10000"
Maximum wait time in milliseconds. Throws TimeoutError if exceeded.
options.settleDurationMs
number
default:"5000"
Wait time in milliseconds before checking if CAPTCHAs appeared. Resets when a CAPTCHA is detected during the period.
options.waitForNetworkSettled
boolean
default:"true"
Whether to wait for network idle before checking solve status.

Returns Returns Promise<T> with the result of the callback function.
Raises
  • TimeoutError — Thrown when timeoutInMs elapses while CAPTCHAs are still being solved.
  • CaptchaSolveError — Thrown when the CAPTCHA solver fails. Contains a CaptchaError with the error code and details.
  • Error — Thrown if something unexpected happens.
Examples
import { waitForCaptchaSolve } from '@intuned/runtime';
import type { Page } from 'playwright';

export async function automation(page: Page, params: any) {
  await page.goto("https://www.google.com/recaptcha/api2/demo");
  await waitForCaptchaSolve(page, {
    timeoutInMs: 15000,
    settleDurationMs: 3000
  });
}

onCaptchaEvent

import { onCaptchaEvent } from '@intuned/runtime';
import type { Page } from 'playwright';
import type { Captcha, CaptchaStatus } from '@intuned/runtime';

export declare function onCaptchaEvent(
  page: Page,
  status: CaptchaStatus,
  f: (captcha: Captcha) => Promise<void> | void
): Promise<void>;
Register a callback for CAPTCHA updates. Subscribe to CAPTCHA updates on a page. The callback fires every time a CAPTCHA with the specified status is observed. The subscription remains active until the page or context is destroyed. Use this for monitoring, logging, or reacting to CAPTCHA status.
Parameters
page
Page
required
Playwright page object.
status
required
The CAPTCHA status to listen for.
f
required
Callback function that executes when the status is observed. Receives a Captcha object as its only argument.

Returns Returns Promise<void>. The function subscribes and resolves immediately. The callback fires each time a CAPTCHA with the specified status is observed.
Raises
  • RuntimeError — Thrown when listeners cannot be attached or the page context is invalid.
Example
import { onCaptchaEvent, type Captcha } from '@intuned/runtime';
import type { Page } from 'playwright';

async function handleCaptchaEvent(captcha: Captcha): Promise<void> {
  console.log(`Captcha ${captcha.id} (tab=${captcha.tabId}) status=${captcha.status} retries=${captcha.retryCount}`);

  if (captcha.status === "solved") {
    console.log(`Solved after ${captcha.retryCount} retries`);
  } else if (captcha.status === "error") {
    console.log(`Solve error: ${captcha.error}`);
  }
}

await onCaptchaEvent(page, 'solved', handleCaptchaEvent);

onceCaptchaEvent

import { onceCaptchaEvent } from '@intuned/runtime';
import type { Page } from 'playwright';
import type { Captcha, CaptchaStatus } from '@intuned/runtime';

export declare function onceCaptchaEvent(
  page: Page,
  status: CaptchaStatus,
  f: (captcha: Captcha) => Promise<void> | void
): Promise<void>;
Register a one-time callback for a CAPTCHA update. Subscribe to CAPTCHA updates on a page. The callback fires once when a CAPTCHA with the specified status is observed, then automatically unsubscribes. Use this when you need to respond only to the next occurrence, such as recording when a CAPTCHA is solved or performing cleanup.
Parameters
page
Page
required
Playwright page object.
status
required
The CAPTCHA status to listen for.
f
required
Callback function that executes when the status is observed. Receives a Captcha object as its only argument.

Returns Returns Promise<void>. The function subscribes and resolves immediately. The callback fires at most once.
Raises
  • RuntimeError — Thrown when listeners cannot be attached or the page context is invalid.
Example
import { onceCaptchaEvent, type Captcha } from '@intuned/runtime';
import type { Page } from 'playwright';

async function handleSolveOnce(captcha: Captcha): Promise<void> {
  console.log(`One-time notify: captcha ${captcha.id} status=${captcha.status} retries=${captcha.retryCount}`);
}

await onceCaptchaEvent(page, 'solved', handleSolveOnce);

removeCaptchaEventListener

import { removeCaptchaEventListener } from '@intuned/runtime';
import type { Page } from 'playwright';
import type { Captcha, CaptchaStatus } from '@intuned/runtime';

export declare function removeCaptchaEventListener(
  page: Page,
  status: CaptchaStatus,
  f: (captcha: Captcha) => Promise<void> | void
): Promise<void>;
Remove a previously registered CAPTCHA callback. Unsubscribe a callback registered using onCaptchaEvent or onceCaptchaEvent. You must pass the same page, status, and callback function that were used to register the callback.
Parameters
page
Page
required
Playwright page object.
status
CaptchaStatus
required
The CAPTCHA status that the listener was registered for.
f
(captcha: Captcha) => any
required
The callback function that was originally registered. Must be the same function reference.

Returns Returns Promise<void>. The function unsubscribes the callback and resolves immediately.
Raises
  • RuntimeError — Thrown when the callback cannot be removed or the page context is invalid.
Example
import { onCaptchaEvent, removeCaptchaEventListener, type Captcha } from '@intuned/runtime';
import type { Page } from 'playwright';

async function handleCaptchaEvent(captcha: Captcha): Promise<void> {
  console.log(`Captcha status: ${captcha.status}`);
}

// Subscribe to CAPTCHA updates
await onCaptchaEvent(page, 'solved', handleCaptchaEvent);

// Later, unsubscribe
await removeCaptchaEventListener(page, 'solved', handleCaptchaEvent);
Note: The callback function reference must match exactly. Anonymous functions cannot be removed. Callbacks registered with onceCaptchaEvent are automatically removed after firing.

Best practices

  • Use waitForCaptchaSolve for most cases where you need to wait for solve completion.
  • Set appropriate timeout values based on CAPTCHA complexity (15-30 seconds typical).
  • Adjust the settle duration for the wait before checking status. Resets when CAPTCHAs are detected.
  • Enable waitForNetworkSettled to wait for network idle before checking solve status.
  • Subscribe to CAPTCHA updates using onCaptchaEvent or onceCaptchaEvent for telemetry and monitoring.
  • Store callback function references if you need to unsubscribe later.

Type reference

Captcha

import type { Captcha, CaptchaStatus, CaptchaError } from '@intuned/runtime';

export type Captcha = {
  id: string;
  tabId: number;
  type: string;
  status: CaptchaStatus;
  retryCount?: number;
  error?: CaptchaError;
};
Properties
id
string
required
Unique identifier for the CAPTCHA observation.
tabId
number
required
Browser tab ID where the CAPTCHA was detected.
type
string
required
CAPTCHA provider type, such as recaptcha, hcaptcha, or cloudflare.
status
required
Current solving state.
retryCount
number
Number of solve attempts made.
error
Error details when status is error, otherwise undefined.

CaptchaStatus

import type { CaptchaStatus } from '@intuned/runtime';

export type CaptchaStatus = "attached" | "solving" | "solved" | "error" | "detached";
Values
  • attached — CAPTCHA element detected in the page. Use this to know when a challenge appears.
  • solving — Solver is actively processing the CAPTCHA.
  • solved — CAPTCHA solved successfully. Resume your workflow.
  • error — Solver failed. Check the error field for details.
  • detached — CAPTCHA element removed from the page. Treat as cancelled.

CaptchaError

import type { CaptchaError, CaptchaErrorCode } from '@intuned/runtime';

export type CaptchaError = {
  code: CaptchaErrorCode;
  error?: any;
};
Properties
code
required
Error code indicating the type of failure.
error
any
Additional error details, if available.

CaptchaErrorCode

import type { CaptchaErrorCode } from '@intuned/runtime';

export type CaptchaErrorCode =
  | "HIT_LIMIT"
  | "MAX_RETRIES"
  | "UNEXPECTED_ERROR"
  | "UNEXPECTED_SERVER_RESPONSE";
Values
  • HIT_LIMIT — Reached billing limits for CAPTCHA solves. See Plans and billing for details on limits and upgrading.
  • MAX_RETRIES — Exceeded maximum retry attempts specified in settings.maxRetries.
  • UNEXPECTED_ERROR — An unexpected error occurred while solving. This is a solver error and not related to your automation.
  • UNEXPECTED_SERVER_RESPONSE — The solver received an unexpected response. This is a solver error and not related to your automation.