--- title: Form Automation | Lightcone description: Fill and submit multi-step forms across legacy web apps without APIs. --- Many business-critical workflows involve filling forms in web apps that don’t have APIs — HR systems, government portals, insurance platforms, procurement tools. Lightcone automates these by driving a real browser, exactly as a human would. ## Fill a simple form fill\_form.py ``` from tzafon import Lightcone client = Lightcone() with client.computer.create(kind="browser") as computer: computer.navigate("https://httpbin.org/forms/post") computer.wait(2) # Fill in form fields by clicking and typing computer.click(300, 180) # Customer name field computer.type("Jane Doe") computer.click(300, 230) # Telephone field computer.type("+1-555-0123") computer.click(300, 280) # Email field computer.type("jane@example.com") # Take a screenshot to verify before submitting result = computer.screenshot() print(f"Preview: {computer.get_screenshot_url(result)}") # Submit computer.click(300, 450) # Submit button computer.wait(2) ``` fill\_form.ts ``` import Lightcone from "@tzafon/lightcone"; const client = new Lightcone(); const computer = await client.computers.create({ kind: "browser" }); const id = computer.id!; try { await client.computers.navigate(id, { url: "https://httpbin.org/forms/post" }); await client.computers.click(id, { x: 300, y: 180 }); await client.computers.type(id, { text: "Jane Doe" }); await client.computers.click(id, { x: 300, y: 230 }); await client.computers.type(id, { text: "+1-555-0123" }); await client.computers.click(id, { x: 300, y: 280 }); await client.computers.type(id, { text: "jane@example.com" }); // Verify before submitting const screenshot = await client.computers.screenshot(id); console.log("Preview:", screenshot.result?.screenshot_url); // Submit await client.computers.click(id, { x: 300, y: 450 }); } finally { await client.computers.delete(id); } ``` Coordinates in these examples are illustrative. Take a screenshot first with `computer.screenshot()` to find the actual field positions on your target form. ## Multi-step form with navigation For forms that span multiple pages (wizards): ``` with client.computer.create(kind="browser") as computer: # Step 1: Personal info computer.navigate("https://app.example.com/apply") computer.wait(2) computer.click(400, 200) computer.type("Jane Doe") computer.click(400, 260) computer.type("jane@example.com") computer.click(500, 400) # "Next" button computer.wait(2) # Step 2: Address computer.click(400, 200) computer.type("123 Main St") computer.click(400, 260) computer.type("San Francisco") computer.click(400, 320) computer.type("CA") computer.click(500, 400) # "Next" button computer.wait(2) # Step 3: Review and submit result = computer.screenshot() print(f"Review: {computer.get_screenshot_url(result)}") computer.click(500, 450) # "Submit" button computer.wait(3) # Confirm submission result = computer.screenshot() print(f"Confirmation: {computer.get_screenshot_url(result)}") ``` ``` const computer = await client.computers.create({ kind: "browser" }); const id = computer.id!; try { // Step 1: Personal info await client.computers.navigate(id, { url: "https://app.example.com/apply" }); await client.computers.click(id, { x: 400, y: 200 }); await client.computers.type(id, { text: "Jane Doe" }); await client.computers.click(id, { x: 400, y: 260 }); await client.computers.type(id, { text: "jane@example.com" }); await client.computers.click(id, { x: 500, y: 400 }); // Next await new Promise((r) => setTimeout(r, 2000)); // Step 2: Address await client.computers.click(id, { x: 400, y: 200 }); await client.computers.type(id, { text: "123 Main St" }); await client.computers.click(id, { x: 500, y: 400 }); // Next await new Promise((r) => setTimeout(r, 2000)); // Step 3: Submit await client.computers.click(id, { x: 500, y: 450 }); } finally { await client.computers.delete(id); } ``` ## Let an AI agent fill the form For complex or dynamic forms, let the agent figure out the field layout: ``` for event in client.agent.tasks.start_stream( instruction=( "Go to https://httpbin.org/forms/post. " "Fill in the form with: " "Customer name: Jane Doe, " "Telephone: +1-555-0123, " "Email: jane@example.com, " "Size: Medium, " "Topping: Bacon. " "Submit the form." ), kind="browser", ): print(event) ``` ``` const stream = await client.agent.tasks.startStream({ instruction: "Go to https://httpbin.org/forms/post. " + "Fill in the form with: Customer name: Jane Doe, " + "Telephone: +1-555-0123, Email: jane@example.com, " + "Size: Medium, Topping: Bacon. Submit the form.", kind: "browser", }); for await (const event of stream) { console.log(event); } ``` The AI agent approach is especially powerful for forms with dynamic fields, dropdowns, date pickers, and other complex UI elements where hardcoding coordinates is fragile. ## Tips - **Screenshot before submitting** — always verify the form is filled correctly before clicking submit - **Use `wait()` between steps** — multi-step forms often have animations or redirects - **Use the [Playwright integration](/integrations/playwright/index.md)** for forms where CSS selectors are more reliable than coordinates - **Use persistent sessions** to stay logged in across multiple form submissions - **Use batch actions** to fill multiple fields in a single request for speed ## See also - [**Web scraping**](/use-cases/web-scraping/index.md) — extract data from pages after form submission - [**Dashboard monitoring**](/use-cases/dashboard-monitoring/index.md) — monitor for form submission results - [**Computers**](/guides/computers/index.md) — session persistence for staying logged in - [**Run an agent**](/guides/run-an-agent/index.md) — let AI handle complex, dynamic forms