--- title: Cross-Application Workflows | Lightcone description: Northstar moves data between applications that don't have APIs — CRM to spreadsheet, email to database, portal to portal. --- Most enterprise work involves copying data between applications that don’t talk to each other — pulling a record from the CRM, entering it into a spreadsheet, filing it in an internal tool, sending a confirmation email. These workflows run on human operators clicking between windows all day. Northstar operates the desktop the same way those operators do, but 24/7, across thousands of instances simultaneously. ## Move data between a web app and a spreadsheet crm\_to\_spreadsheet.py ``` from tzafon import Lightcone client = Lightcone() for event in client.agent.tasks.start_stream( instruction=( "Open Firefox and go to https://crm.example.com. Log in with 'ops@example.com' / 'ops123'. " "Navigate to the Contacts page. Find the contact 'Acme Corp'. " "Note their email address, phone number, and account status. " "Then open LibreOffice Calc. " "Enter the headers 'Company', 'Email', 'Phone', 'Status' in row 1. " "Enter the Acme Corp data in row 2. " "Save the file as /tmp/acme-export.csv." ), kind="desktop", max_steps=40, ): print(event) ``` crm\_to\_spreadsheet.ts ``` import Lightcone from "@tzafon/lightcone"; const client = new Lightcone(); const stream = await client.agent.tasks.startStream({ instruction: "Open Firefox and go to https://crm.example.com. Log in with 'ops@example.com' / 'ops123'. " + "Navigate to the Contacts page. Find the contact 'Acme Corp'. " + "Note their email address, phone number, and account status. " + "Then open LibreOffice Calc. " + "Enter the headers 'Company', 'Email', 'Phone', 'Status' in row 1. " + "Enter the Acme Corp data in row 2. " + "Save the file as /tmp/acme-export.csv.", kind: "desktop", max_steps: 40, }); for await (const event of stream) { console.log(event); } ``` Northstar opens the CRM in a browser, finds the data, switches to a spreadsheet application, enters it, and saves the file. Two applications, one instruction. ## Process a batch across systems ``` # Process each record: look it up in system A, enter it in system B records = [ {"name": "Acme Corp", "action": "renew"}, {"name": "Globex Inc", "action": "upgrade"}, {"name": "Initech LLC", "action": "cancel"}, ] for record in records: print(f"Processing {record['name']}...") for event in client.agent.tasks.start_stream( instruction=( f"Open Firefox and go to https://portal.example.com. " f"Search for '{record['name']}'. " f"Click on their account. " f"Click the '{record['action'].title()}' button. " f"Confirm the action. " f"Report whether it succeeded." ), kind="desktop", max_steps=20, ): print(f" {event}") ``` ``` const records = [ { name: "Acme Corp", action: "renew" }, { name: "Globex Inc", action: "upgrade" }, { name: "Initech LLC", action: "cancel" }, ]; for (const record of records) { console.log(`Processing ${record.name}...`); const stream = await client.agent.tasks.startStream({ instruction: `Open Firefox and go to https://portal.example.com. ` + `Search for '${record.name}'. Click on their account. ` + `Click the '${record.action}' button. Confirm the action. ` + `Report whether it succeeded.`, kind: "desktop", max_steps: 20, }); for await (const event of stream) { console.log(` ${JSON.stringify(event)}`); } } ``` ## Use persistent state for multi-session workflows Install software, configure the environment once, and reuse it across runs: ``` # First run: set up the environment with client.computer.create(kind="desktop", persistent=True) as computer: # Install tools Northstar will need client.computers.exec.sync(computer.id, command="apt-get install -y libreoffice") saved_id = computer.id print(f"Environment ready: {saved_id}") # Subsequent runs: reuse the environment for event in client.agent.tasks.start_stream( instruction="Open LibreOffice Calc, load /tmp/data.csv, and add a 'Total' column that sums columns B through D", kind="desktop", environment_id=saved_id, ): print(event) ``` ``` // First run: set up const computer = await client.computers.create({ kind: "desktop", persistent: true, }); await client.computers.exec.sync(computer.id!, { command: "apt-get install -y libreoffice", }); const savedId = computer.id!; await client.computers.delete(savedId); // Subsequent runs: reuse const stream = await client.agent.tasks.startStream({ instruction: "Open LibreOffice Calc, load /tmp/data.csv, and add a 'Total' column that sums columns B through D", kind: "desktop", environment_id: savedId, }); for await (const event of stream) { console.log(event); } ``` ## Why Northstar for cross-app workflows These workflows are hard to automate traditionally because: - **No APIs** — many enterprise apps (especially legacy ones) don’t have APIs, or the APIs don’t expose what you need - **Multiple applications** — the workflow spans 2-5 different systems, each with its own interface - **Human judgment** — the operator needs to read one screen and decide what to enter in another Northstar handles all three. It operates the desktop like a human — switching between apps, reading data from one, entering it in another — but without getting tired, making typos, or taking lunch breaks. ## See also - [**Tasks**](/guides/tasks/index.md) — run workflows with natural language instructions - [**Shell commands**](/guides/shell-commands/index.md) — combine GUI automation with CLI tools - [**Lightcone OS**](/guides/lightcone-os/index.md) — install the software your workflows need - [**Computers**](/guides/computers/index.md) — persistent state for long-running workflows