--- title: Healthcare Portal Data Extraction subtitle: Extract patient demographics and billing data from OpenEMR slug: cookbooks/healthcare-portal-data --- This cookbook extracts two datasets from [OpenEMR](https://www.open-emr.org/), an open-source EHR, using the public demo at `https://demo.openemr.io/openemr/index.php`: 1. **Patient demographics** from Patient/Client > Finder 2. **Encounter billing data** from Reports > Visits > Superbill **Demo credentials:** `admin` / `pass` (resets daily at 8:00 AM UTC) --- ## Prerequisites - A [Skyvern Cloud](https://app.skyvern.com) account or [self-hosted](/self-hosted/overview) deployment - The Skyvern SDK (for API usage) ```bash Python pip install skyvern ``` ```bash TypeScript npm install @skyvern/client ``` --- ## Why a single task isn't enough A basic task pointed at OpenEMR with a vague prompt will partially work, but hits four problems in production: ```python Python result = await client.run_task( url="https://demo.openemr.io/openemr/index.php", prompt="Log in and extract the patient list", ) ``` ```typescript TypeScript const result = await client.runTask({ body: { url: "https://demo.openemr.io/openemr/index.php", prompt: "Log in and extract the patient list", }, }); ``` | Problem | Impact | |---------|--------| | No proxy | Production EHR portals sit behind WAFs that block datacenter IPs | | Login every run | Wastes steps, fragile with session complexity | | Vague navigation | OpenEMR uses iframes and dynamic menus — needs explicit goals | | No pagination | Only gets page 1 of multi-page results | The sections below solve each one. --- ## Residential proxies Route the browser through a residential IP to bypass WAF/bot detection. The demo works without one, but production portals require it. In the run panel, expand **Advanced Settings** and set **Proxy Location** to a country (e.g., **United States**). ```python Python result = await client.run_task( url="https://demo.openemr.io/openemr/index.php", prompt="Log in with username 'admin' and password 'pass', confirm the Calendar page loads", proxy_location="RESIDENTIAL", ) ``` ```typescript TypeScript const result = await client.runTask({ body: { url: "https://demo.openemr.io/openemr/index.php", prompt: "Log in with username 'admin' and password 'pass', confirm the Calendar page loads", proxy_location: "RESIDENTIAL", }, }); ``` See [Proxy & Geolocation](/going-to-production/proxy-geolocation) for all available locations. --- ## Browser profiles Log in once, save the browser state as a profile, and skip login on future runs. Go to **Workflows** in the sidebar and create a new workflow. Add a **Navigation** block with URL `https://demo.openemr.io/openemr/index.php` and goal: "Log in with username 'admin' and password 'pass'. Confirm the Calendar page loads." On the **Start** node, expand the settings and enable **Save & Reuse Session**. Set **Proxy Location** to a country (e.g., **United States**). Run the workflow and wait for it to complete. Browser profile creation is done via the API. Use the `create_browser_profile` call from the API/SDK tab with the completed workflow run ID. Name it `openemr-demo-admin`. ```python Python import asyncio from skyvern import Skyvern async def main(): client = Skyvern(api_key="YOUR_API_KEY") # 1. Create workflow that saves browser state workflow = await client.create_workflow( json_definition={ "title": "OpenEMR Login", "persist_browser_session": True, "workflow_definition": { "parameters": [], "blocks": [ { "block_type": "navigation", "label": "login", "url": "https://demo.openemr.io/openemr/index.php", "navigation_goal": ( "Log in with username 'admin' and password 'pass'. " "Confirm the Calendar page or main dashboard loads." ), } ], }, } ) # 2. Run with residential proxy run = await client.run_workflow( workflow_id=workflow.workflow_permanent_id, proxy_location="RESIDENTIAL", wait_for_completion=True, ) print(f"Login: {run.status}") # completed # 3. Save profile (retry while session archives) profile = None for attempt in range(10): try: profile = await client.create_browser_profile( name="openemr-demo-admin", workflow_run_id=run.run_id, ) break except Exception as e: if "persisted" in str(e).lower() and attempt < 9: await asyncio.sleep(2) continue raise print(f"Profile: {profile.browser_profile_id}") asyncio.run(main()) ``` ```typescript TypeScript import { Skyvern } from "@skyvern/client"; async function main() { const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! }); // 1. Create workflow that saves browser state const workflow = await client.createWorkflow({ body: { json_definition: { title: "OpenEMR Login", persist_browser_session: true, workflow_definition: { parameters: [], blocks: [ { block_type: "navigation", label: "login", url: "https://demo.openemr.io/openemr/index.php", navigation_goal: "Log in with username 'admin' and password 'pass'. " + "Confirm the Calendar page or main dashboard loads.", }, ], }, }, }, }); // 2. Run with residential proxy const run = await client.runWorkflow({ body: { workflow_id: workflow.workflow_permanent_id, proxy_location: "RESIDENTIAL", }, waitForCompletion: true, }); console.log(`Login: ${run.status}`); // 3. Save profile (retry while session archives) let profile; for (let attempt = 0; attempt < 10; attempt++) { try { profile = await client.createBrowserProfile({ name: "openemr-demo-admin", workflow_run_id: run.run_id, }); break; } catch (e) { if (String(e).toLowerCase().includes("persisted") && attempt < 9) { await new Promise((r) => setTimeout(r, 2000)); continue; } throw e; } } console.log(`Profile: ${profile.browser_profile_id}`); } main(); ``` `persist_browser_session` is a workflow definition property — set it when creating the workflow, not when running it. See [Browser Profiles](/optimization/browser-profiles) for the full lifecycle. --- ## Extract patient demographics Navigate to **Patient/Client > Finder** and extract the results table. Create a workflow with two blocks: 1. **Navigation** block — URL: `https://demo.openemr.io/openemr/index.php`. Goal: "Click Patient/Client in the top menu, then click Finder. Click Search to display all patients." 2. **Extraction** block — Goal: "Extract all patient rows from the Patient Finder results table." Paste the patient schema into **Data Schema**. On the **Start** node, set **Proxy Location** to a country (e.g., **United States**). Run the workflow. ```python Python import asyncio from skyvern import Skyvern PATIENT_SCHEMA = { "type": "object", "properties": { "patients": { "type": "array", "description": "Patient rows from the Patient Finder results table", "items": { "type": "object", "properties": { "name": {"type": "string", "description": "Patient full name (Last, First)"}, "pid": {"type": "string", "description": "Patient ID number"}, "dob": {"type": "string", "description": "Date of birth (YYYY-MM-DD)"}, "phone_home": {"type": "string", "description": "Home phone number"}, }, }, }, }, } async def main(): client = Skyvern(api_key="YOUR_API_KEY") run = await client.run_task( url="https://demo.openemr.io/openemr/index.php", prompt=( "Click Patient/Client in the top menu, then click Finder. " "Click Search to display all patients." ), data_extraction_schema=PATIENT_SCHEMA, proxy_location="RESIDENTIAL", browser_session_id="YOUR_SESSION_ID", ) while run.status not in ["completed", "failed", "terminated", "timed_out", "canceled"]: await asyncio.sleep(5) run = await client.get_run(run.run_id) print(run.output) asyncio.run(main()) ``` ```typescript TypeScript import { Skyvern } from "@skyvern/client"; const PATIENT_SCHEMA = { type: "object", properties: { patients: { type: "array", description: "Patient rows from the Patient Finder results table", items: { type: "object", properties: { name: { type: "string", description: "Patient full name (Last, First)" }, pid: { type: "string", description: "Patient ID number" }, dob: { type: "string", description: "Date of birth (YYYY-MM-DD)" }, phone_home: { type: "string", description: "Home phone number" }, }, }, }, }, } as const; async function main() { const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! }); let run = await client.runTask({ body: { url: "https://demo.openemr.io/openemr/index.php", prompt: "Click Patient/Client in the top menu, then click Finder. " + "Click Search to display all patients.", data_extraction_schema: PATIENT_SCHEMA, proxy_location: "RESIDENTIAL", browser_session_id: "YOUR_SESSION_ID", }, }); while (!["completed", "failed", "terminated", "timed_out", "canceled"].includes(run.status)) { await new Promise((r) => setTimeout(r, 5000)); run = await client.getRun(run.run_id); } console.log(JSON.stringify(run.output, null, 2)); } main(); ``` **Example output:** ```json { "patients": [ { "name": "Belford, Phil", "pid": "1", "dob": "1972-02-09", "phone_home": "333-444-2222" }, { "name": "Underwood, Susan Ardmore", "pid": "2", "dob": "1967-02-08", "phone_home": "4443332222" }, { "name": "Moore, Wanda", "pid": "3", "dob": "2007-02-18", "phone_home": null } ] } ``` The demo resets daily and community users add test patients, so exact records may differ. Browser profiles cannot be used directly with standalone tasks. Create a [browser session](/optimization/browser-sessions) from the profile first, then pass the session ID. See [Pagination with browser sessions](#pagination-with-browser-sessions) below for the full pattern. --- ## Extract encounter billing data Navigate to **Reports > Visits > Superbill**, set a date range, and extract the report. Create a workflow with two blocks: 1. **Navigation** block — Goal: "Click Reports in the top menu, then Visits, then Superbill. Set the From date to 2020-01-01 and the To date to today. Click Submit." 2. **Extraction** block — Goal: "Extract all encounter rows from the Superbill report." Paste the encounter schema into **Data Schema**. On the **Start** node, set **Proxy Location** to a country (e.g., **United States**). Run the workflow. ```python Python ENCOUNTER_SCHEMA = { "type": "object", "properties": { "encounters": { "type": "array", "description": "Encounter rows from the Superbill report", "items": { "type": "object", "properties": { "patient_name": {"type": "string", "description": "Patient name"}, "encounter_date": {"type": "string", "description": "Date of encounter (YYYY-MM-DD)"}, "provider": {"type": "string", "description": "Provider name"}, "billing_code": {"type": "string", "description": "CPT or billing code"}, "code_description": {"type": "string", "description": "Description of the billing code"}, "charge": {"type": "number", "description": "Fee amount in USD"}, }, }, }, }, } run = await client.run_task( url="https://demo.openemr.io/openemr/index.php", prompt=( "Click Reports in the top menu, then Visits, then Superbill. " "Set the From date to 2020-01-01 and the To date to today. Click Submit." ), data_extraction_schema=ENCOUNTER_SCHEMA, proxy_location="RESIDENTIAL", browser_session_id="YOUR_SESSION_ID", ) ``` ```typescript TypeScript const ENCOUNTER_SCHEMA = { type: "object", properties: { encounters: { type: "array", description: "Encounter rows from the Superbill report", items: { type: "object", properties: { patient_name: { type: "string", description: "Patient name" }, encounter_date: { type: "string", description: "Date of encounter (YYYY-MM-DD)" }, provider: { type: "string", description: "Provider name" }, billing_code: { type: "string", description: "CPT or billing code" }, code_description: { type: "string", description: "Description of the billing code" }, charge: { type: "number", description: "Fee amount in USD" }, }, }, }, }, } as const; let run = await client.runTask({ body: { url: "https://demo.openemr.io/openemr/index.php", prompt: "Click Reports in the top menu, then Visits, then Superbill. " + "Set the From date to 2020-01-01 and the To date to today. Click Submit.", data_extraction_schema: ENCOUNTER_SCHEMA, proxy_location: "RESIDENTIAL", browser_session_id: "YOUR_SESSION_ID", }, }); ``` **Example output:** ```json { "encounters": [ { "patient_name": "Phil Lopez", "encounter_date": "2024-06-01", "provider": "Administrator Administrator", "billing_code": "99213", "code_description": "Office/outpatient visit, est patient, low complexity", "charge": 50.00 } ] } ``` --- ## Pagination with browser sessions A [Browser Profile](/optimization/browser-profiles) is a saved snapshot. A [Browser Session](/optimization/browser-sessions) is a live browser instance that persists between tasks. Use sessions to paginate: extract page 1, click Next, extract page 2. Go to **Browsers** in the sidebar. Click **Create Session**. Set **Proxy Location** to a country (e.g., **United States**) and configure the timeout. Run a task against the session: "Click Patient/Client > Finder. Click Search. Extract all patient rows." Run another task against the same session: "Click Next to go to the next page. Extract all patient rows." Repeat until no more results. ```python Python import asyncio from skyvern import Skyvern PATIENT_SCHEMA = { "type": "object", "properties": { "patients": { "type": "array", "description": "Patient rows from the current page", "items": { "type": "object", "properties": { "name": {"type": "string", "description": "Patient full name (Last, First)"}, "pid": {"type": "string", "description": "Patient ID number"}, "dob": {"type": "string", "description": "Date of birth (YYYY-MM-DD)"}, "phone_home": {"type": "string", "description": "Home phone number"}, }, }, }, }, } async def extract_page(client, session_id, page_num): prompt = ( "Click Patient/Client in the top menu, then click Finder. " "Click Search to display all patients. " "Extract all patient rows from the results table." ) if page_num == 1 else ( "Click Next to go to the next page of results. " "Extract all patient rows from the table." ) run = await client.run_task( url="https://demo.openemr.io/openemr/index.php", prompt=prompt, browser_session_id=session_id, data_extraction_schema=PATIENT_SCHEMA, ) while run.status not in ["completed", "failed", "terminated", "timed_out", "canceled"]: await asyncio.sleep(5) run = await client.get_run(run.run_id) return run async def main(): client = Skyvern(api_key="YOUR_API_KEY") session = await client.create_browser_session( browser_profile_id="YOUR_PROFILE_ID", proxy_location="RESIDENTIAL", ) all_patients = [] for page in range(1, 11): run = await extract_page(client, session.browser_session_id, page) if run.status != "completed": break patients = run.output.get("patients", []) if not patients: break all_patients.extend(patients) print(f"Page {page}: {len(patients)} patients ({len(all_patients)} total)") print(f"Done: {len(all_patients)} patients") asyncio.run(main()) ``` ```typescript TypeScript import { Skyvern } from "@skyvern/client"; const PATIENT_SCHEMA = { type: "object", properties: { patients: { type: "array", description: "Patient rows from the current page", items: { type: "object", properties: { name: { type: "string", description: "Patient full name (Last, First)" }, pid: { type: "string", description: "Patient ID number" }, dob: { type: "string", description: "Date of birth (YYYY-MM-DD)" }, phone_home: { type: "string", description: "Home phone number" }, }, }, }, }, } as const; async function extractPage(client: Skyvern, sessionId: string, pageNum: number) { const prompt = pageNum === 1 ? "Click Patient/Client in the top menu, then click Finder. " + "Click Search to display all patients. " + "Extract all patient rows from the results table." : "Click Next to go to the next page of results. " + "Extract all patient rows from the table."; let run = await client.runTask({ body: { url: "https://demo.openemr.io/openemr/index.php", prompt, browser_session_id: sessionId, data_extraction_schema: PATIENT_SCHEMA, }, }); while (!["completed", "failed", "terminated", "timed_out", "canceled"].includes(run.status)) { await new Promise((r) => setTimeout(r, 5000)); run = await client.getRun(run.run_id); } return run; } async function main() { const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! }); const session = await client.createBrowserSession({ browser_profile_id: "YOUR_PROFILE_ID", proxy_location: "RESIDENTIAL", }); const allPatients: any[] = []; for (let page = 1; page <= 10; page++) { const run = await extractPage(client, session.browser_session_id, page); if (run.status !== "completed") break; const patients = run.output?.patients ?? []; if (patients.length === 0) break; allPatients.push(...patients); console.log(`Page ${page}: ${patients.length} patients (${allPatients.length} total)`); } console.log(`Done: ${allPatients.length} patients`); } main(); ``` **Expected output:** ``` Page 1: 25 patients (25 total) Page 2: 18 patients (43 total) Done: 43 patients ``` You cannot use `browser_profile_id` and `browser_session_id` in the same request. Use the profile to create the session, then pass only the session ID to tasks. --- ## Error handling OpenEMR can timeout or show session-expired pages. Use `error_code_mapping` on workflow blocks to classify failures, and `max_retries` to retry automatically. On each Navigation and Extraction block, expand **Advanced Settings** and enable **Error Messages**. Add this JSON: ```json { "session_expired": "Session expired, login required, or access denied page" } ``` `max_retries` is only available via the API. In the Cloud UI, Skyvern uses its default retry behavior. For fine-grained retry control, use the API/SDK approach. Set `error_code_mapping` and `max_retries` directly on workflow blocks: ```json { "block_type": "extraction", "label": "extract_patients", "data_extraction_goal": "Extract all patient rows from the table", "error_code_mapping": { "session_expired": "Session expired, login required, or access denied page" }, "max_retries": 3 } ``` For standalone tasks, handle retries in your calling code: ```python Python async def run_with_retry(client, session_id, page_num, max_retries=3): for attempt in range(max_retries + 1): run = await extract_page(client, session_id, page_num) if run.status == "completed": return run is_session_error = "session" in (run.failure_reason or "").lower() if is_session_error and attempt < max_retries: await asyncio.sleep(2 ** attempt * 5) continue return run return run ``` ```typescript TypeScript async function runWithRetry( client: Skyvern, sessionId: string, pageNum: number, maxRetries = 3 ) { let run; for (let attempt = 0; attempt <= maxRetries; attempt++) { run = await extractPage(client, sessionId, pageNum); if (run.status === "completed") return run; const isSessionError = (run.failure_reason ?? "").toLowerCase().includes("session"); if (isSessionError && attempt < maxRetries) { await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 5000)); continue; } return run; } return run!; } ``` See [Error Handling](/going-to-production/error-handling) and [CAPTCHA & Bot Detection](/going-to-production/captcha-bot-detection) for more. --- ## Complete workflow This workflow combines everything: navigate to the Patient Finder, extract demographics, navigate to Superbill, and extract billing data — with error recovery and residential proxy. Go to **Workflows** and create a new workflow named "OpenEMR Daily Extract." On the **Start** node, enable **Save & Reuse Session** and set **Proxy Location** to a country (e.g., **United States**). Add a **Navigation** block. Set URL to `https://demo.openemr.io/openemr/index.php` and goal: "Click Patient/Client > Finder. Click Search to display all patients. If a login page appears, log in with 'admin'/'pass'." In **Advanced Settings**, enable **Error Messages** and add `{"session_expired": "Session expired, login required, or access denied page"}`. Add an **Extraction** block. Set goal: "Extract all patient rows from the Patient Finder results table." Paste the patient schema into **Data Schema**. Add another **Navigation** block. Set goal: "Click Reports > Visits > Superbill. Set From to 2020-01-01, To to today. Click Submit." Add the same error messages mapping. Add another **Extraction** block. Set goal: "Extract all encounter rows from the Superbill report." Paste the encounter schema into **Data Schema**. Click **Run**. The workflow navigates to the Patient Finder, extracts demographics, then navigates to Superbill and extracts billing data. For multi-page results, combine with the [pagination pattern](#pagination-with-browser-sessions) above. ```python Python import asyncio from skyvern import Skyvern PATIENT_SCHEMA = { "type": "object", "properties": { "patients": { "type": "array", "description": "Patient rows from the Patient Finder results table", "items": { "type": "object", "properties": { "name": {"type": "string", "description": "Patient full name (Last, First)"}, "pid": {"type": "string", "description": "Patient ID number"}, "dob": {"type": "string", "description": "Date of birth (YYYY-MM-DD)"}, "phone_home": {"type": "string", "description": "Home phone number"}, }, }, }, }, } ENCOUNTER_SCHEMA = { "type": "object", "properties": { "encounters": { "type": "array", "description": "Encounter rows from the Superbill report", "items": { "type": "object", "properties": { "patient_name": {"type": "string", "description": "Patient name"}, "encounter_date": {"type": "string", "description": "Date of encounter (YYYY-MM-DD)"}, "provider": {"type": "string", "description": "Provider name"}, "billing_code": {"type": "string", "description": "CPT or billing code"}, "code_description": {"type": "string", "description": "Description of the billing code"}, "charge": {"type": "number", "description": "Fee amount in USD"}, }, }, }, }, } SESSION_ERROR = "Session expired, login required, or access denied page" async def main(): client = Skyvern(api_key="YOUR_API_KEY") workflow = await client.create_workflow( json_definition={ "title": "OpenEMR Daily Extract", "persist_browser_session": True, "workflow_definition": { "parameters": [], "blocks": [ { "block_type": "navigation", "label": "open_patient_finder", "url": "https://demo.openemr.io/openemr/index.php", "navigation_goal": ( "Click Patient/Client in the top menu, then click Finder. " "Click Search to display all patients. " "If a login page appears, log in with username 'admin' and password 'pass'." ), "error_code_mapping": {"session_expired": SESSION_ERROR}, "max_retries": 3, }, { "block_type": "extraction", "label": "extract_patients", "data_extraction_goal": "Extract all patient rows from the Patient Finder results table", "data_schema": PATIENT_SCHEMA, "error_code_mapping": {"session_expired": SESSION_ERROR}, "max_retries": 2, }, { "block_type": "navigation", "label": "open_superbill", "navigation_goal": ( "Click Reports in the top menu, then Visits, then Superbill. " "Set the From date to 2020-01-01 and the To date to today. Click Submit." ), "error_code_mapping": {"session_expired": SESSION_ERROR}, "max_retries": 3, }, { "block_type": "extraction", "label": "extract_encounters", "data_extraction_goal": "Extract all encounter rows from the Superbill report", "data_schema": ENCOUNTER_SCHEMA, "error_code_mapping": {"session_expired": SESSION_ERROR}, "max_retries": 2, }, ], }, } ) print(f"Workflow: {workflow.workflow_permanent_id}") run = await client.run_workflow( workflow_id=workflow.workflow_permanent_id, browser_profile_id="YOUR_PROFILE_ID", proxy_location="RESIDENTIAL", wait_for_completion=True, ) print(f"Status: {run.status}") print(f"Output: {run.output}") asyncio.run(main()) ``` ```typescript TypeScript import { Skyvern } from "@skyvern/client"; const PATIENT_SCHEMA = { type: "object", properties: { patients: { type: "array", description: "Patient rows from the Patient Finder results table", items: { type: "object", properties: { name: { type: "string", description: "Patient full name (Last, First)" }, pid: { type: "string", description: "Patient ID number" }, dob: { type: "string", description: "Date of birth (YYYY-MM-DD)" }, phone_home: { type: "string", description: "Home phone number" }, }, }, }, }, } as const; const ENCOUNTER_SCHEMA = { type: "object", properties: { encounters: { type: "array", description: "Encounter rows from the Superbill report", items: { type: "object", properties: { patient_name: { type: "string", description: "Patient name" }, encounter_date: { type: "string", description: "Date of encounter (YYYY-MM-DD)" }, provider: { type: "string", description: "Provider name" }, billing_code: { type: "string", description: "CPT or billing code" }, code_description: { type: "string", description: "Description of the billing code" }, charge: { type: "number", description: "Fee amount in USD" }, }, }, }, }, } as const; const SESSION_ERROR = "Session expired, login required, or access denied page"; async function main() { const client = new Skyvern({ apiKey: process.env.SKYVERN_API_KEY! }); const workflow = await client.createWorkflow({ body: { json_definition: { title: "OpenEMR Daily Extract", persist_browser_session: true, workflow_definition: { parameters: [], blocks: [ { block_type: "navigation", label: "open_patient_finder", url: "https://demo.openemr.io/openemr/index.php", navigation_goal: "Click Patient/Client in the top menu, then click Finder. " + "Click Search to display all patients. " + "If a login page appears, log in with username 'admin' and password 'pass'.", error_code_mapping: { session_expired: SESSION_ERROR }, max_retries: 3, }, { block_type: "extraction", label: "extract_patients", data_extraction_goal: "Extract all patient rows from the Patient Finder results table", data_schema: PATIENT_SCHEMA, error_code_mapping: { session_expired: SESSION_ERROR }, max_retries: 2, }, { block_type: "navigation", label: "open_superbill", navigation_goal: "Click Reports in the top menu, then Visits, then Superbill. " + "Set the From date to 2020-01-01 and the To date to today. Click Submit.", error_code_mapping: { session_expired: SESSION_ERROR }, max_retries: 3, }, { block_type: "extraction", label: "extract_encounters", data_extraction_goal: "Extract all encounter rows from the Superbill report", data_schema: ENCOUNTER_SCHEMA, error_code_mapping: { session_expired: SESSION_ERROR }, max_retries: 2, }, ], }, }, }, }); console.log(`Workflow: ${workflow.workflow_permanent_id}`); const run = await client.runWorkflow({ body: { workflow_id: workflow.workflow_permanent_id, browser_profile_id: "YOUR_PROFILE_ID", proxy_location: "RESIDENTIAL", }, waitForCompletion: true, }); console.log(`Status: ${run.status}`); console.log(`Output: ${JSON.stringify(run.output, null, 2)}`); } main(); ``` | Technique | Purpose | |-----------|---------| | Residential proxy | Bypass WAF/bot detection | | Browser profile | Skip login on every run | | Navigation goals | Explicit menu clicks for iframe-based UI | | JSON schemas | Consistent, structured output | | Session reuse | Paginate multi-page results | | Error mapping + retries | Recover from session timeouts | The OpenEMR demo resets daily at 8:00 AM UTC, so profiles expire every day. In production, re-run your login workflow weekly or whenever extractions fail with auth errors. See [Browser Profiles](/optimization/browser-profiles) for the refresh pattern. --- ## Resources Full lifecycle: create, refresh, and delete saved browser state All proxy locations and country-specific routing options Securely store and use login credentials Error code mapping, failure classification, and retry strategies JSON schema design and the interactive schema builder