Skip to main content

Advanced Hybrid Automation Configurations

This page is the comprehensive reference for all Browserless.liveURL options and advanced patterns. It covers bandwidth tuning, viewport control, programmatic session management, recording integration, CAPTCHA fallback handling, multi-stage workflows, and error recovery.

If you are new to hybrid automation, start with the Hybrid Automation guide.

Bandwidth and Quality Optimization

Fine-tune live sessions for different network conditions and device capabilities.

const { liveURL } = await cdp.send('Browserless.liveURL', {
quality: 30, // Optimized for mobile/slow connections
type: 'jpeg', // Better compression than PNG
timeout: 300000, // 5 minutes for complex workflows
});

Advanced Viewport Control

Control viewport behavior and browser UI visibility for live sessions.

By default, the live URL viewport resizes to match the end user's screen. Set resizable: false to lock the browser at its current viewport dimensions. The live URL client preserves the aspect ratio.

const { liveURL } = await cdp.send('Browserless.liveURL', {
resizable: false, // Maintain fixed viewport
interactable: true, // Allow user interaction
showBrowserInterface: false, // Hide browser tabs and UI
});

Multi-Tab Workflows

For workflows involving multiple tabs or separate windows, set showBrowserInterface: true to stream all open tabs. End users can switch between tabs as needed. This option injects UI overlay code into the page, which may increase CAPTCHA challenge likelihood on some sites.

const { liveURL } = await cdp.send('Browserless.liveURL', {
showBrowserInterface: true, // Show all tabs and browser UI
quality: 50,
timeout: 300000, // 5 minutes for complex workflows
});

Programmatic Session Management

Take full control over when and how sessions end. The Browserless.closeLiveURL command lets your script close a live session based on page state, while Browserless.liveComplete fires when a user closes the session manually.

const cdp = await page.createCDPSession();
const { liveURL, liveURLId } = await cdp.send('Browserless.liveURL', {
timeout: LIVE_URL_TIMEOUT
});
console.log('Click for live experience:', liveURL);

const completionPromise = Promise.race([
// Scenario 1: User closes LiveURL manually
new Promise(resolve => cdp.on('Browserless.liveComplete', () => {
console.log('Live URL was closed by user');
resolve('user_closed');
})),
// Scenario 2: A selector appears, programmatically close LiveURL
page.waitForSelector('.someSelector', { timeout: 0 }).then(async () => {
console.log('Selector detected, closing LiveURL programmatically');
await cdp.send('Browserless.closeLiveURL', { liveURLId });
return 'selector_detected';
})
]);

completionPromise.then((result) => {
console.log(`LiveURL closed via: ${result}`);
console.log('Cleaning up browser...');
browser.close().then(() => {
console.log('Script completed successfully');
process.exit(0);
});
});

Read-Only Monitoring Sessions

Create view-only sessions for compliance monitoring or training by setting interactable: false. The viewer sees the browser but cannot click, type, or interact.

const { liveURL } = await cdp.send('Browserless.liveURL', {
interactable: false, // View-only mode
quality: 80, // Higher quality for monitoring
timeout: 1800000, // 30 minutes for extended monitoring
});

// Share with compliance team or supervisors
console.log('Monitoring URL (read-only):', liveURL);

Session Recording Integration

Combine screen recording with live sessions to capture an audit trail. Set record=true as a query parameter when connecting to the browser endpoint. See Screen Recording for full details.

// Start recording before creating live session
await cdp.send('Browserless.startRecording');

const { liveURL } = await cdp.send('Browserless.liveURL', {
timeout: 300000
});

// Wait for session completion
await new Promise(resolve => cdp.on('Browserless.liveComplete', resolve));

// Save recording (see recording docs for details)
const recording = await cdp.send('Browserless.stopRecording');
fs.writeFileSync('audit-trail.webm', Buffer.from(recording.value, 'binary'));

CAPTCHA Handling with Hybrid Fallback

Try automated CAPTCHA solving first, then fall back to a live URL for human intervention if the solver fails. Listen for the Browserless.captchaFound event to trigger this flow. For full CAPTCHA solver documentation, see CAPTCHA Solving.

// Set up CAPTCHA detection before navigation
cdp.on('Browserless.captchaFound', async () => {
console.log('CAPTCHA detected, attempting automatic solve');

const { solved } = await cdp.send('Browserless.solveCaptcha');

if (!solved) {
// Fall back to human intervention
const { liveURL } = await cdp.send('Browserless.liveURL', {
timeout: 120000 // 2 minutes for CAPTCHA solving
});
console.log('Manual CAPTCHA solving needed:', liveURL);
}
});

Multi-Stage Workflows

Chain multiple hybrid sessions for complex business processes. Each stage alternates between automated steps and human handoffs.

const multiStageWorkflow = async () => {
const browser = await puppeteer.connect({
browserWSEndpoint: 'wss://production-sfo.browserless.io?token=YOUR_TOKEN'
});

try {
const page = await browser.newPage();
const cdp = await page.createCDPSession();

await page.goto('https://practice.expandtesting.com/inputs');
await page.waitForSelector('input[name="input-number"]');

// Stage 1: User fills the number input via liveURL
let { liveURL } = await cdp.send('Browserless.liveURL', {
timeout: 180000 // 3 minutes for number entry
});
console.log('Stage 1 - Enter a number:', liveURL);
await new Promise(resolve => cdp.on('Browserless.liveComplete', resolve));

// Stage 2: Automated filling of text and password fields
await page.type('input[name="input-text"]', 'Automated text entry');
await page.type('input[name="input-password"]', 'SecurePass123');
console.log('Stage 2 - Automated text and password fields completed');

// Stage 3: User fills the date input via liveURL
({ liveURL } = await cdp.send('Browserless.liveURL', {
timeout: 300000 // 5 minutes for date selection
}));
console.log('Stage 3 - Select a date:', liveURL);
await new Promise(resolve => cdp.on('Browserless.liveComplete', resolve));

console.log('Multi-stage workflow completed successfully');

} finally {
await browser.close();
}
};

Error Recovery and Resilience

Implement retry logic for production hybrid automation. This helper retries on transient failures but exits immediately on permanent errors like invalid tokens or rate limits.

import puppeteer from 'puppeteer-core';

const createResilientLiveSession = async (cdp, options = {}) => {
const { timeout = 300000, quality = 50, maxRetries = 2 } = options;

for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const { liveURL, liveURLId } = await cdp.send('Browserless.liveURL', {
timeout,
quality,
type: 'jpeg'
});

console.log(`Live session created on attempt ${attempt}`);
return { liveURL, liveURLId };

} catch (error) {
console.log(`Live session attempt ${attempt} failed:`, error.message);

// Don't retry on permanent errors
if (error.message.includes('Invalid token') ||
error.message.includes('Rate limit exceeded')) {
throw error;
}

if (attempt === maxRetries) {
throw new Error(`Failed to create live session after ${maxRetries} attempts: ${error.message}`);
}

// Brief wait before retry
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
};

(async () => {
let browser = null;

try {
const BROWSERLESS_TOKEN = 'YOUR_API_TOKEN_HERE';

browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io?token=${BROWSERLESS_TOKEN}`,
});

const page = await browser.newPage();
await page.goto('https://practicetestautomation.com/practice-test-login/');

const cdp = await page.createCDPSession();

const { liveURL, liveURLId } = await createResilientLiveSession(cdp, {
timeout: 300000,
quality: 50,
maxRetries: 3
});

console.log('Live URL created:', liveURL);
console.log('Live URL ID:', liveURLId);

await new Promise((resolve) => {
cdp.on('Browserless.liveComplete', resolve);
});

console.log('Live session completed');

} catch (error) {
console.error('Error:', error);
} finally {
if (browser) {
await browser.close();
}
}
})();

Next Steps