1 line
88 KiB
JSON
1 line
88 KiB
JSON
{"repo": "asmaalgarably/invoicex-automation", "files": {"uipath-automation/README.md": {"86b2a89f18255238fe9210ae33362bfe6eb99cc7": {"t": 1785747278.173697, "n": 20175, "h": "3f199b1567ad4453", "c": "# Multi-Platform Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod and ERPNext without IXP or RPA runtime dependency.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to one or more destinations.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n- ERPNext posting is handled through Frappe REST APIs. It creates Purchase Invoice drafts and attaches the source invoice image/PDF; v1 never submits accounting entries.\n\n---\n\n**Jump to:** [System Architecture](#system-architecture) · [Run Locally](#run-locally) · [HTTPS Tunnels](#temporary-https-tunnels) · [Local Env](#local-env) · [UiPath Resources](#uipath-resources) · [ERPNext](#erpnext-destination) · [API Endpoints](#api-endpoints) · [Chrome Extension](#chrome-extension) · [Current Blockers](#current-blockers) · [Verify](#verify)\n\n---\n\n## System Architecture\n\nThe system is built on a **three-layer architecture** with UiPath Maestro as the state machine orchestrator, API Workflows as thin polling messengers, and an Express server as the worker.\n\n### Architecture\n\nThis graph captures every component, every API route, every Maestro task, and every data flow in the production system.\n\n```mermaid\n%%{init: {\"theme\": \"neutral\", \"themeVariables\": {\"fontSize\": \"13px\", \"primaryColor\": \"#4f8cff\"}}}%%\nflowchart TB\n subgraph External[\"🌐 External Actors\"]\n PWA[\"📱 Phone PWA\\nInvoice photo/PDF capture\\nQR TLV scan\"]\n FINANCE[\"👤 Finance User\\nReview & approve/reject\"]\n EXCEPTION[\"👤 Exception Handler\\nResolve failures\"]\n QOYOD_SITE[\"🌍 Qoyod.com\\nBill creation page\"]\n end\n\n subgraph IntakeAPI[\"🖥️ Backend API (Express)\"]\n direction TB\n API_CAPTURE[\"POST /api/captures\\nSingle invoice upload\"]\n API_BATCH[\"POST /api/batches\\nBatch upload (≤50 files)\"]\n API_BATCH_GET[\"GET /api/batches/:batchId\"]\n API_BATCH_LIST[\"GET /api/batches\"]\n API_BULK_REVIEW[\"POST /api/batches/:batchId/bulk-review\"]\n\n API_CASE_PROGRESS[\"GET /api/case/batches/:batchId/progress\"]\n API_CASE_STAGE[\"POST /api/case/batches/:batchId/stage\"]\n API_CASE_EXCEPTION[\"POST /api/case/batches/:batchId/exception\"]\n API_CASE_CLOSE[\"POST /api/case/batches/:batchId/close\"]\n API_CASE_EXTRACTION[\"POST /api/case/batches/:batchId/extraction/start\"]\n API_CASE_TASK[\"POST /api/case/batches/:batchId/task\"]\n\n API_EXTRACT_START[\"POST /api/extraction/jobs/:jobId/start\"]\n API_EXTRACT_RESULT[\"POST /api/extraction/jobs/:jobId/result\"]\n API_REVIEW[\"POST /api/jobs/:jobId/review\"]\n API_DEST_POST[\"POST /api/jobs/:jobId/destinations/:platform/post\"]\n API_DESTINATIONS[\"GET /api/destinations\"]\n\n API_FILL_CLAIM[\"POST /api/fill/jobs/claim-next\"]\n API_FILL_STATUS[\"POST /api/fill/jobs/:jobId/status\"]\n API_FILL_SOURCE[\"GET /api/fill/jobs/:jobId/source\"]\n\n API_MAPPINGS[\"GET /api/mappings\\nPOST /api/mappings\\nDELETE /api/mappings/:ruleId\"]\n API_APPLY_MAPPINGS[\"POST /api/batches/:batchId/apply-mappings\"]\n API_ERP_PREFLIGHT[\"GET /api/destinations/erpnext/preflight\"]\n end\n\n subgraph MaestroCase[\"📋 UiPath Maestro Case\"]\n direction TB\n MC_TRIGGER[\"Trigger\\nStart Invoice Batch Intake\"]\n\n MC_STAGE1[\"Stage 1: Capture Intake\\nRegister Capture Payload\"]\n MC_STAGE2[\"Stage 2: Extraction &\\nReconciliation\"]\n MC_STAGE3[\"Stage 3: Finance Review\\n& Mapping\"]\n MC_STAGE4[\"Stage 4: Destination\\nPosting\"]\n MC_STAGE5[\"Stage 5: Qoyod\\nDrafting\"]\n MC_EXCEPTION[\"Stage 6: Exception\\nResolution\"]\n MC_CLOSED[\"Stage 7: Closed\\nRecord Case Closure\"]\n\n MC_TRIGGER -->|Entered| MC_STAGE1\n MC_STAGE1 -->|Completed| MC_STAGE2\n MC_STAGE2 -->|no error| MC_STAGE3\n MC_STAGE2 -->|has error| MC_EXCEPTION\n MC_STAGE3 -->|approved| MC_STAGE4\n MC_STAGE3 -->|rejected| MC_CLOSED\n MC_STAGE3 -->|error| MC_EXCEPTION\n MC_STAGE4 -->|Qoyod next| MC_STAGE5\n MC_STAGE4 -->|closed| MC_CLOSED\n MC_STAGE4 -->|error| MC_EXCEPTION\n MC_STAGE5 -->|draft saved| MC_CLOSED\n MC_STAGE5 -->|error| MC_EXCEPTION\n MC_EXCEPTION -->|resolved| MC_CLOSED\n end\n\n subgraph ApiWorkflows[\"⚡ API Workflow Definitions\"]\n direction TB\n AW_REGISTER[\"RegisterCapturePayload\\nPOST → /api/case/batches/{id}/stage\"]\n AW_EXTRACTION[\"StartAndWaitExtraction\\nPOST → start + poll progress\\nuntil complete/error/timeout\"]\n AW_POSTING[\"PersistReviewAndWaitPosting\\nPoll progress for ERPNext\\nposting completion\"]\n AW_QOYOD[\"WaitForQoyodExtensionDraft\\nPoll progress for Qoyod\\ndraft save completion\"]\n AW_CLOSURE[\"RecordCaseClosure\\nPOST → /api/case/batches/{id}/close\"]\n end\n\n subgraph DataStore[\"💾 Data Store\"]\n JOB_STORE[\"Job Store\\nIn-memory Map\"]\n UPLOAD_DIR[\"Upload Directory\\nLocal filesystem\"]\n MAPPING_RULES[\"Mapping Rules\\nSupplier→Item/Expense\"]\n end\n\n subgraph UiPathCLI[\"🔌 UiPath Orchestrator CLI\"]\n UIP_BUCKET[\"or bucket-files upload\\nAttachments to bucket\"]\n UIP_QUEUE[\"or queue-items add\\nInvoiceIntake queue\"]\n UIP_CASE[\"uip maestro case process run\\nStart Maestro Case\"]\n end\n\n subgraph LLM[\"🧠 LLM Extraction\"]\n direction LR\n OPENAI[\"OpenAI GPT\"]\n DEEPSEEK[\"DeepSeek\"]\n EXTERNAL[\"External Service\"]\n MOCK[\"Mock (dev only)\"]\n end\n\n subgraph Reconciliation[\"✅ Reconciliation Engine\"]\n RECON[\"reconcileDraft()\\nQR ↔ OCR validation\\nRequired fields\\nDuplicate check\\nConfidence scoring\"]\n end\n\n subgraph Mappings[\"🔗 Auto-Mapping Engine\"]\n MAP_ENGINE[\"applyMappingRules()\\nSupplier + text matching\\nScoring & ranking\"]\n end\n\n subgraph Destinations[\"📤 Destination Posting\"]\n EN_POST[\"ERPNext Post Engine\\ncreateErpNextPurchaseInvoiceDraft()\"]\n EN_PREFLIGHT[\"ERPNext Preflight\\nSupplier + Item validation\"]\n end\n\n subgraph ChromeExtension[\"🧩 Chrome Extension (Qoyod Filler)\"]\n direction TB\n SP_SIDEPANEL[\"Side Panel\\nClaim, Fill, Save Draft\"]\n SP_CONTENT[\"Content Script\\nDOM manipulation\"]\n SP_CALIBRATION[\"Calibration Engine\\nLearn CSS selectors\"]\n SP_SELECTORS[\"Selector Profile\\nSaved per-user\"]\n end\n\n subgraph QoyodSaaS[\"📊 Qoyod Accounting\"]\n Q_FORM[\"Bill Creation Form\\nVendor, Lines, Tax,\\nAttachments\"]\n Q_DRAFT[\"Draft Bills\\nSaved, not posted\"]\n end\n\n subgraph WebApp[\"🌐 Finance Web App (React)\"]\n WA_BATCH_VIEW[\"Batch List View\"]\n WA_JOB_VIEW[\"Invoice Detail View\\nEdit draft, mapping,\\napprove/reject\"]\n WA_MAPPINGS[\"Mapping Rules Manager\\nCRUD for rules\"]\n WA_STATUS[\"Status Dashboard\\nJob status, destinations\"]\n end\n\n PWA -->|\"POST /api/captures\\ndocument + qrPayload\"| API_CAPTURE\n PWA -->|\"POST /api/batches\\ndocuments[] + qrPayloads[]\"| API_BATCH\n\n API_CAPTURE -->|create job| JOB_STORE\n API_BATCH -->|create batch + jobs| JOB_STORE\n\n API_BATCH -->|start Maestro Case| UIP_CASE\n UIP_CASE -->|caseInstanceId| MC_TRIGGER\n\n MaestroCase <-->|HTTP callbacks via x-case-token| IntakeAPI\n\n MC_STAGE1 -->|triggers| AW_REGISTER\n AW_REGISTER -->|POST stage| API_CASE_STAGE\n API_CASE_STAGE -->|update batch| JOB_STORE\n\n MC_STAGE2 -->|triggers| AW_EXTRACTION\n AW_EXTRACTION -->|POST start| API_CASE_EXTRACTION\n API_CASE_EXTRACTION -->|extract| LLM\n LLM -->|POST result| API_EXTRACT_RESULT\n API_EXTRACT_RESULT -->|validate| RECON\n RECON -->|update job| JOB_STORE\n AW_EXTRACTION -->|GET progress| API_CASE_PROGRESS\n API_CASE_PROGRESS --> JOB_STORE\n\n MC_STAGE3 -->|triggers| FINANCE\n FINANCE <-->|\"Action Center task\"| WebApp\n WebApp -->|\"POST /api/jobs/:jobId/review\"| API_REVIEW\n WebApp -->|\"POST /api/batches/:batchId/bulk-review\"| API_BULK_REVIEW\n API_REVIEW --> JOB_STORE\n API_BULK_REVIEW --> JOB_STORE\n\n MC_STAGE4 -->|triggers| AW_POSTING\n AW_POSTING -->|GET progress| API_CASE_PROGRESS\n API_CASE_PROGRESS --> JOB_STORE\n API_BULK_REVIEW -->|auto-post ERPNext| EN_POST\n EN_POST -->|\"POST /api/destinations/erpnext/preflight\"| EN_PREFLIGHT\n EN_POST -->|create draft| JOB_STORE\n\n MC_STAGE5 -->|triggers| AW_QOYOD\n AW_QOYOD -->|GET progress| API_CASE_PROGRESS\n\n SP_SIDEPANEL -->|claim-next| API_FILL_CLAIM\n API_FILL_CLAIM --> JOB_STORE\n SP_SIDEPANEL -->|fill| SP_CONTENT\n SP_CONTENT -->|DOM manipulation| QOYOD_SITE\n QOYOD_SITE --> Q_FORM\n SP_SIDEPANEL -->|save draft| SP_CONTENT\n SP_CONTENT -->|confirm| Q_DRAFT\n SP_SIDEPANEL -->|status update| API_FILL_STATUS\n API_FILL_STATUS --> JOB_STORE\n SP_SIDEPANEL -->|fetch source| API_FILL_SOURCE\n\n MC_EXCEPTION -->|triggers| EXCEPTION\n EXCEPTION <-->|Action Center| WebApp\n EXCEPTION -->|resolve| API_CASE_EXCEPTION\n\n MC_CLOSED -->|triggers| AW_CLOSURE\n AW_CLOSURE -->|POST close| API_CASE_CLOSE\n API_CASE_CLOSE -->|mark closed| JOB_STORE\n\n API_DEST_POST --> EN_POST\n API_ERP_PREFLIGHT --> EN_PREFLIGHT\n\n API_MAPPINGS <--> MAPPING_RULES\n API_APPLY_MAPPINGS --> MAP_ENGINE\n MAP_ENGINE --> MAPPING_RULES\n\n WA_BATCH_VIEW --> API_BATCH_LIST\n WA_JOB_VIEW --> API_BATCH_GET\n WA_MAPPINGS --> API_MAPPINGS\n\n UIP_BUCKET -->|upload attachment| JOB_STORE\n UIP_QUEUE -->|queue item| JOB_STORE\n\n style PWA fill:#c8e6c9,stroke:#388e3c,color:#1b5e20\n style FINANCE fill:#ffe0b2,stroke:#ef6c00,color:#bf360c\n style EXCEPTION fill:#ffccbc,stroke:#d84315,color:#bf360c\n style QOYOD_SITE fill:#b2dfdb,stroke:#00897b,color:#004d40\n style MaestroCase fill:#bbdefb,stroke:#1976d2,color:#0d47a1,stroke-width:2px\n style IntakeAPI fill:#e3f2fd,stroke:#1565c0,color:#0d47a1\n style ApiWorkflows fill:#e8eaf6,stroke:#3949ab,color:#1a237e\n style LLM fill:#e1bee7,stroke:#7b1fa2,color:#4a148c\n style ChromeExtension fill:#d1c4e9,stroke:#5e35b1,color:#311b92\n style WebApp fill:#fff9c4,stroke:#fdd835,color:#f57f17\n style Destinations fill:#f8bbd0,stroke:#c2185b,color:#880e4f\n```\n\n### Stage-by-Stage Data Flow\n\n| Stage | Maestro Task | API Workflow | What Happens |\n|---|---|---|---|\n| **Capture Intake** | `RegisterCapturePayload` | `QoyodCaseRegisterCapturePayload` | Records batch → `POST /api/case/batches/{id}/stage` |\n| **Extraction & Reconciliation** | `StartAndWaitExtraction` | `QoyodCaseStartAndWaitExtraction` | Starts LLM extraction, polls `GET /progress`, runs QR↔OCR reconciliation |\n| **Finance Review & Mapping** | `ReviewCorrectAndMapInvoice` | Action Center task | Human reviews in web app, corrects fields, approves destinations |\n| **Destination Posting** | `PersistReviewAndWaitPosting` | `QoyodCasePersistReviewAndWaitPosting` | Polls for ERPNext draft completion (auto-created via API) |\n| **Qoyod Drafting** | `WaitForQoyodExtensionDraftSave` | `QoyodCaseWaitForQoyodExtensionDraft` | Polls for Chrome extension to save draft in Qoyod |\n| **Exception Resolution** | `ResolveInvoiceIntakeException` | Action Center task | Human resolves rejections/failures, can re-route or close |\n| **Closed** | `RecordCaseClosure` | `QoyodCaseRecordCaseClosure` | Final `POST /api/case/batches/{id}/close` notifies backend |\n\n### Exception Routing\n\n| Error Source | Condition | Routes To |\n|---|---|---|\n| Extraction | `errorCode !== ''` after extraction | Exception Resolution |\n| Finance review | `reviewDecision === 'rejected'` or `errorCode !== ''` | Exception Resolution |\n| Destination posting | `errorCode !== ''` after ERPNext | Exception Resolution |\n| Qoyod drafting | `errorCode !== ''` after extension fill | Exception Resolution |\n| Manual | User selects exception stage manually | Exception Resolution |\n\n### Closing Paths\n\n1. **Happy path:** Capture → Extraction → Review → ERPNext → Qoyod Draft → **Closed**\n2. **Rejection:** Capture → Extraction → Review (reject) → **Closed**\n3. **Exception resolved:** Capture → Extraction → Exception → **Closed**\n4. **ERPNext-only:** Capture → Extraction → Review → ERPNext → **Closed** (skips Qoyod)\n\n---\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see [`docs/USER_GUIDE.md`](docs/USER_GUIDE.md).\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start -SyncMaestro\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing and used by Action Center review links through `PUBLIC_WEB_APP_URL`.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL and web tunnel URL, then verifies that the running backend and Maestro preflight see those URLs. Tunnel URLs are temporary; restart tunnels and rerun sync before each live Maestro demo.\n\nIf you started tunnels without `-SyncMaestro`, run:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\sync-maestro-tunnel.ps1\n```\n\nRun a live Case callback smoke test:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-tunnel-smoke.ps1 -BatchName \"demo test\"\n```\n\nTunnel rotation does not require redeploying the UiPath package because the current tunnel URL is passed as a Case start input. See `docs/MAESTRO_TUNNEL_ROTATION_FINDINGS.md` for the root cause and recovery details.\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:PUBLIC_WEB_APP_URL=\"http://localhost:5173\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n$env:INVOICE_DESTINATIONS=\"qoyod,erpnext\"\n$env:ERPNEXT_BASE_URL=\"https://your-sandbox.example\"\n$env:ERPNEXT_API_KEY=\"<api-key>\"\n$env:ERPNEXT_API_SECRET=\"<api-secret>\"\n$env:ERPNEXT_COMPANY=\"<company>\"\n$env:ERPNEXT_DEFAULT_EXPENSE_ACCOUNT=\"<expense-account>\"\n$env:ERPNEXT_DEFAULT_COST_CENTER=\"<cost-center>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL. Action Center review links also need `PUBLIC_WEB_APP_URL` to be a public HTTPS URL when reviewers are outside the local browser.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. In live mode, API Workflow tasks call the backend to start extraction, poll review/post/fill state, and record stage callbacks. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## ERPNext Destination\n\nConfigure ERPNext against a sandbox/staging site first. Required values:\n\n- `ERPNEXT_BASE_URL`\n- `ERPNEXT_API_KEY`\n- `ERPNEXT_API_SECRET`\n- `ERPNEXT_COMPANY`\n- `ERPNEXT_DEFAULT_EXPENSE_ACCOUNT`\n- `ERPNEXT_DEFAULT_COST_CENTER`\n\nOptional values:\n\n- `ERPNEXT_DEFAULT_ITEM_CODE` for sites that require an item code on every Purchase Invoice row.\n- `ERPNEXT_PURCHASE_TAXES_AND_CHARGES_TEMPLATE` or `ERPNEXT_VAT_ACCOUNT_HEAD` for tax handling.\n- `ERPNEXT_TEST_SUPPLIER` for live smoke tests.\n\n`ERPNEXT_SUBMIT_AFTER_POST` must stay `false` in v1. ERPNext output is a draft Purchase Invoice plus the uploaded source attachment.\n\nRun live sandbox validation explicitly:\n\n```powershell\n$env:LIVE_ERPNEXT_TEST=\"true\"\n$env:LIVE_UIPATH_TEST=\"true\"\n$env:ERPNEXT_TEST_SUPPLIER=\"<existing-sandbox-supplier>\"\nnpm run smoke:live\n```\n\nThe ERPNext smoke test creates a uniquely numbered `UIPATH-TEST-<timestamp>` draft and leaves it in the sandbox for manual inspection.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDestinations:\n\n```http\nGET /api/destinations\nGET /api/destinations/erpnext/preflight\nPOST /api/jobs/{jobId}/destinations/erpnext/post\nPOST /api/case/jobs/{jobId}/destinations/erpnext/post\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/runtime/config\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n- ERPNext requires existing sandbox master data: Supplier, Company, Account, Cost Center, and tax/item setup as configured.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm run smoke:live\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "72c1a611635b31e563339f923de7c453d7b3087a": {"t": 1785772100.7876232, "n": 20203, "h": "8eb5a87301fd5f1d", "c": "# Multi-Platform Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod and ERPNext without IXP or RPA runtime dependency.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to one or more destinations.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n- ERPNext posting is handled through Frappe REST APIs. It creates Purchase Invoice drafts and attaches the source invoice image/PDF; v1 never submits accounting entries.\n\n---\n\n**Jump to:** [System Architecture](#system-architecture) · [Run Locally](#run-locally) · [HTTPS Tunnels](#temporary-https-tunnels) · [Local Env](#local-env) · [UiPath Resources](#uipath-resources) · [ERPNext](#erpnext-destination) · [API Endpoints](#api-endpoints) · [Chrome Extension](#chrome-extension) · [Current Blockers](#current-blockers) · [Verify](#verify)\n\n---\n\n## System Architecture\n\nThe system is built on a **three-layer architecture** with UiPath Maestro as the state machine orchestrator, API Workflows as thin polling messengers, and an Express server as the worker that does all the real work.\n\n### Architecture\n\nThis graph captures every component, every API route, every Maestro task, and every data flow in the production system.\n\n```mermaid\n%%{init: {\"theme\": \"neutral\", \"themeVariables\": {\"fontSize\": \"13px\", \"primaryColor\": \"#4f8cff\"}}}%%\nflowchart TB\n subgraph External[\"🌐 External Actors\"]\n PWA[\"📱 Phone PWA\\nInvoice photo/PDF capture\\nQR TLV scan\"]\n FINANCE[\"👤 Finance User\\nReview & approve/reject\"]\n EXCEPTION[\"👤 Exception Handler\\nResolve failures\"]\n QOYOD_SITE[\"🌍 Qoyod.com\\nBill creation page\"]\n end\n\n subgraph IntakeAPI[\"🖥️ Backend API (Express)\"]\n direction TB\n API_CAPTURE[\"POST /api/captures\\nSingle invoice upload\"]\n API_BATCH[\"POST /api/batches\\nBatch upload (≤50 files)\"]\n API_BATCH_GET[\"GET /api/batches/:batchId\"]\n API_BATCH_LIST[\"GET /api/batches\"]\n API_BULK_REVIEW[\"POST /api/batches/:batchId/bulk-review\"]\n\n API_CASE_PROGRESS[\"GET /api/case/batches/:batchId/progress\"]\n API_CASE_STAGE[\"POST /api/case/batches/:batchId/stage\"]\n API_CASE_EXCEPTION[\"POST /api/case/batches/:batchId/exception\"]\n API_CASE_CLOSE[\"POST /api/case/batches/:batchId/close\"]\n API_CASE_EXTRACTION[\"POST /api/case/batches/:batchId/extraction/start\"]\n API_CASE_TASK[\"POST /api/case/batches/:batchId/task\"]\n\n API_EXTRACT_START[\"POST /api/extraction/jobs/:jobId/start\"]\n API_EXTRACT_RESULT[\"POST /api/extraction/jobs/:jobId/result\"]\n API_REVIEW[\"POST /api/jobs/:jobId/review\"]\n API_DEST_POST[\"POST /api/jobs/:jobId/destinations/:platform/post\"]\n API_DESTINATIONS[\"GET /api/destinations\"]\n\n API_FILL_CLAIM[\"POST /api/fill/jobs/claim-next\"]\n API_FILL_STATUS[\"POST /api/fill/jobs/:jobId/status\"]\n API_FILL_SOURCE[\"GET /api/fill/jobs/:jobId/source\"]\n\n API_MAPPINGS[\"GET /api/mappings\\nPOST /api/mappings\\nDELETE /api/mappings/:ruleId\"]\n API_APPLY_MAPPINGS[\"POST /api/batches/:batchId/apply-mappings\"]\n API_ERP_PREFLIGHT[\"GET /api/destinations/erpnext/preflight\"]\n end\n\n subgraph MaestroCase[\"📋 UiPath Maestro Case\"]\n direction TB\n MC_TRIGGER[\"Trigger\\nStart Invoice Batch Intake\"]\n\n MC_STAGE1[\"Stage 1: Capture Intake\\nRegister Capture Payload\"]\n MC_STAGE2[\"Stage 2: Extraction &\\nReconciliation\"]\n MC_STAGE3[\"Stage 3: Finance Review\\n& Mapping\"]\n MC_STAGE4[\"Stage 4: Destination\\nPosting\"]\n MC_STAGE5[\"Stage 5: Qoyod\\nDrafting\"]\n MC_EXCEPTION[\"Stage 6: Exception\\nResolution\"]\n MC_CLOSED[\"Stage 7: Closed\\nRecord Case Closure\"]\n\n MC_TRIGGER -->|Entered| MC_STAGE1\n MC_STAGE1 -->|Completed| MC_STAGE2\n MC_STAGE2 -->|no error| MC_STAGE3\n MC_STAGE2 -->|has error| MC_EXCEPTION\n MC_STAGE3 -->|approved| MC_STAGE4\n MC_STAGE3 -->|rejected| MC_CLOSED\n MC_STAGE3 -->|error| MC_EXCEPTION\n MC_STAGE4 -->|Qoyod next| MC_STAGE5\n MC_STAGE4 -->|closed| MC_CLOSED\n MC_STAGE4 -->|error| MC_EXCEPTION\n MC_STAGE5 -->|draft saved| MC_CLOSED\n MC_STAGE5 -->|error| MC_EXCEPTION\n MC_EXCEPTION -->|resolved| MC_CLOSED\n end\n\n subgraph ApiWorkflows[\"⚡ API Workflow Definitions\"]\n direction TB\n AW_REGISTER[\"RegisterCapturePayload\\nPOST → /api/case/batches/{id}/stage\"]\n AW_EXTRACTION[\"StartAndWaitExtraction\\nPOST → start + poll progress\\nuntil complete/error/timeout\"]\n AW_POSTING[\"PersistReviewAndWaitPosting\\nPoll progress for ERPNext\\nposting completion\"]\n AW_QOYOD[\"WaitForQoyodExtensionDraft\\nPoll progress for Qoyod\\ndraft save completion\"]\n AW_CLOSURE[\"RecordCaseClosure\\nPOST → /api/case/batches/{id}/close\"]\n end\n\n subgraph DataStore[\"💾 Data Store\"]\n JOB_STORE[\"Job Store\\nIn-memory Map\"]\n UPLOAD_DIR[\"Upload Directory\\nLocal filesystem\"]\n MAPPING_RULES[\"Mapping Rules\\nSupplier→Item/Expense\"]\n end\n\n subgraph UiPathCLI[\"🔌 UiPath Orchestrator CLI\"]\n UIP_BUCKET[\"or bucket-files upload\\nAttachments to bucket\"]\n UIP_QUEUE[\"or queue-items add\\nInvoiceIntake queue\"]\n UIP_CASE[\"uip maestro case process run\\nStart Maestro Case\"]\n end\n\n subgraph LLM[\"🧠 LLM Extraction\"]\n direction LR\n OPENAI[\"OpenAI GPT\"]\n DEEPSEEK[\"DeepSeek\"]\n EXTERNAL[\"External Service\"]\n MOCK[\"Mock (dev only)\"]\n end\n\n subgraph Reconciliation[\"✅ Reconciliation Engine\"]\n RECON[\"reconcileDraft()\\nQR ↔ OCR validation\\nRequired fields\\nDuplicate check\\nConfidence scoring\"]\n end\n\n subgraph Mappings[\"🔗 Auto-Mapping Engine\"]\n MAP_ENGINE[\"applyMappingRules()\\nSupplier + text matching\\nScoring & ranking\"]\n end\n\n subgraph Destinations[\"📤 Destination Posting\"]\n EN_POST[\"ERPNext Post Engine\\ncreateErpNextPurchaseInvoiceDraft()\"]\n EN_PREFLIGHT[\"ERPNext Preflight\\nSupplier + Item validation\"]\n end\n\n subgraph ChromeExtension[\"🧩 Chrome Extension (Qoyod Filler)\"]\n direction TB\n SP_SIDEPANEL[\"Side Panel\\nClaim, Fill, Save Draft\"]\n SP_CONTENT[\"Content Script\\nDOM manipulation\"]\n SP_CALIBRATION[\"Calibration Engine\\nLearn CSS selectors\"]\n SP_SELECTORS[\"Selector Profile\\nSaved per-user\"]\n end\n\n subgraph QoyodSaaS[\"📊 Qoyod Accounting\"]\n Q_FORM[\"Bill Creation Form\\nVendor, Lines, Tax,\\nAttachments\"]\n Q_DRAFT[\"Draft Bills\\nSaved, not posted\"]\n end\n\n subgraph WebApp[\"🌐 Finance Web App (React)\"]\n WA_BATCH_VIEW[\"Batch List View\"]\n WA_JOB_VIEW[\"Invoice Detail View\\nEdit draft, mapping,\\napprove/reject\"]\n WA_MAPPINGS[\"Mapping Rules Manager\\nCRUD for rules\"]\n WA_STATUS[\"Status Dashboard\\nJob status, destinations\"]\n end\n\n PWA -->|\"POST /api/captures\\ndocument + qrPayload\"| API_CAPTURE\n PWA -->|\"POST /api/batches\\ndocuments[] + qrPayloads[]\"| API_BATCH\n\n API_CAPTURE -->|create job| JOB_STORE\n API_BATCH -->|create batch + jobs| JOB_STORE\n\n API_BATCH -->|start Maestro Case| UIP_CASE\n UIP_CASE -->|caseInstanceId| MC_TRIGGER\n\n MaestroCase <-->|HTTP callbacks via x-case-token| IntakeAPI\n\n MC_STAGE1 -->|triggers| AW_REGISTER\n AW_REGISTER -->|POST stage| API_CASE_STAGE\n API_CASE_STAGE -->|update batch| JOB_STORE\n\n MC_STAGE2 -->|triggers| AW_EXTRACTION\n AW_EXTRACTION -->|POST start| API_CASE_EXTRACTION\n API_CASE_EXTRACTION -->|extract| LLM\n LLM -->|POST result| API_EXTRACT_RESULT\n API_EXTRACT_RESULT -->|validate| RECON\n RECON -->|update job| JOB_STORE\n AW_EXTRACTION -->|GET progress| API_CASE_PROGRESS\n API_CASE_PROGRESS --> JOB_STORE\n\n MC_STAGE3 -->|triggers| FINANCE\n FINANCE <-->|\"Action Center task\"| WebApp\n WebApp -->|\"POST /api/jobs/:jobId/review\"| API_REVIEW\n WebApp -->|\"POST /api/batches/:batchId/bulk-review\"| API_BULK_REVIEW\n API_REVIEW --> JOB_STORE\n API_BULK_REVIEW --> JOB_STORE\n\n MC_STAGE4 -->|triggers| AW_POSTING\n AW_POSTING -->|GET progress| API_CASE_PROGRESS\n API_CASE_PROGRESS --> JOB_STORE\n API_BULK_REVIEW -->|auto-post ERPNext| EN_POST\n EN_POST -->|\"POST /api/destinations/erpnext/preflight\"| EN_PREFLIGHT\n EN_POST -->|create draft| JOB_STORE\n\n MC_STAGE5 -->|triggers| AW_QOYOD\n AW_QOYOD -->|GET progress| API_CASE_PROGRESS\n\n SP_SIDEPANEL -->|claim-next| API_FILL_CLAIM\n API_FILL_CLAIM --> JOB_STORE\n SP_SIDEPANEL -->|fill| SP_CONTENT\n SP_CONTENT -->|DOM manipulation| QOYOD_SITE\n QOYOD_SITE --> Q_FORM\n SP_SIDEPANEL -->|save draft| SP_CONTENT\n SP_CONTENT -->|confirm| Q_DRAFT\n SP_SIDEPANEL -->|status update| API_FILL_STATUS\n API_FILL_STATUS --> JOB_STORE\n SP_SIDEPANEL -->|fetch source| API_FILL_SOURCE\n\n MC_EXCEPTION -->|triggers| EXCEPTION\n EXCEPTION <-->|Action Center| WebApp\n EXCEPTION -->|resolve| API_CASE_EXCEPTION\n\n MC_CLOSED -->|triggers| AW_CLOSURE\n AW_CLOSURE -->|POST close| API_CASE_CLOSE\n API_CASE_CLOSE -->|mark closed| JOB_STORE\n\n API_DEST_POST --> EN_POST\n API_ERP_PREFLIGHT --> EN_PREFLIGHT\n\n API_MAPPINGS <--> MAPPING_RULES\n API_APPLY_MAPPINGS --> MAP_ENGINE\n MAP_ENGINE --> MAPPING_RULES\n\n WA_BATCH_VIEW --> API_BATCH_LIST\n WA_JOB_VIEW --> API_BATCH_GET\n WA_MAPPINGS --> API_MAPPINGS\n\n UIP_BUCKET -->|upload attachment| JOB_STORE\n UIP_QUEUE -->|queue item| JOB_STORE\n\n style PWA fill:#c8e6c9,stroke:#388e3c,color:#1b5e20\n style FINANCE fill:#ffe0b2,stroke:#ef6c00,color:#bf360c\n style EXCEPTION fill:#ffccbc,stroke:#d84315,color:#bf360c\n style QOYOD_SITE fill:#b2dfdb,stroke:#00897b,color:#004d40\n style MaestroCase fill:#bbdefb,stroke:#1976d2,color:#0d47a1,stroke-width:2px\n style IntakeAPI fill:#e3f2fd,stroke:#1565c0,color:#0d47a1\n style ApiWorkflows fill:#e8eaf6,stroke:#3949ab,color:#1a237e\n style LLM fill:#e1bee7,stroke:#7b1fa2,color:#4a148c\n style ChromeExtension fill:#d1c4e9,stroke:#5e35b1,color:#311b92\n style WebApp fill:#fff9c4,stroke:#fdd835,color:#f57f17\n style Destinations fill:#f8bbd0,stroke:#c2185b,color:#880e4f\n```\n\n### Stage-by-Stage Data Flow\n\n| Stage | Maestro Task | API Workflow | What Happens |\n|---|---|---|---|\n| **Capture Intake** | `RegisterCapturePayload` | `QoyodCaseRegisterCapturePayload` | Records batch → `POST /api/case/batches/{id}/stage` |\n| **Extraction & Reconciliation** | `StartAndWaitExtraction` | `QoyodCaseStartAndWaitExtraction` | Starts LLM extraction, polls `GET /progress`, runs QR↔OCR reconciliation |\n| **Finance Review & Mapping** | `ReviewCorrectAndMapInvoice` | Action Center task | Human reviews in web app, corrects fields, approves destinations |\n| **Destination Posting** | `PersistReviewAndWaitPosting` | `QoyodCasePersistReviewAndWaitPosting` | Polls for ERPNext draft completion (auto-created via API) |\n| **Qoyod Drafting** | `WaitForQoyodExtensionDraftSave` | `QoyodCaseWaitForQoyodExtensionDraft` | Polls for Chrome extension to save draft in Qoyod |\n| **Exception Resolution** | `ResolveInvoiceIntakeException` | Action Center task | Human resolves rejections/failures, can re-route or close |\n| **Closed** | `RecordCaseClosure` | `QoyodCaseRecordCaseClosure` | Final `POST /api/case/batches/{id}/close` notifies backend |\n\n### Exception Routing\n\n| Error Source | Condition | Routes To |\n|---|---|---|\n| Extraction | `errorCode !== ''` after extraction | Exception Resolution |\n| Finance review | `reviewDecision === 'rejected'` or `errorCode !== ''` | Exception Resolution |\n| Destination posting | `errorCode !== ''` after ERPNext | Exception Resolution |\n| Qoyod drafting | `errorCode !== ''` after extension fill | Exception Resolution |\n| Manual | User selects exception stage manually | Exception Resolution |\n\n### Closing Paths\n\n1. **Happy path:** Capture → Extraction → Review → ERPNext → Qoyod Draft → **Closed**\n2. **Rejection:** Capture → Extraction → Review (reject) → **Closed**\n3. **Exception resolved:** Capture → Extraction → Exception → **Closed**\n4. **ERPNext-only:** Capture → Extraction → Review → ERPNext → **Closed** (skips Qoyod)\n\n---\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see [`docs/USER_GUIDE.md`](docs/USER_GUIDE.md).\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start -SyncMaestro\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing and used by Action Center review links through `PUBLIC_WEB_APP_URL`.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL and web tunnel URL, then verifies that the running backend and Maestro preflight see those URLs. Tunnel URLs are temporary; restart tunnels and rerun sync before each live Maestro demo.\n\nIf you started tunnels without `-SyncMaestro`, run:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\sync-maestro-tunnel.ps1\n```\n\nRun a live Case callback smoke test:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-tunnel-smoke.ps1 -BatchName \"demo test\"\n```\n\nTunnel rotation does not require redeploying the UiPath package because the current tunnel URL is passed as a Case start input. See `docs/MAESTRO_TUNNEL_ROTATION_FINDINGS.md` for the root cause and recovery details.\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:PUBLIC_WEB_APP_URL=\"http://localhost:5173\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n$env:INVOICE_DESTINATIONS=\"qoyod,erpnext\"\n$env:ERPNEXT_BASE_URL=\"https://your-sandbox.example\"\n$env:ERPNEXT_API_KEY=\"<api-key>\"\n$env:ERPNEXT_API_SECRET=\"<api-secret>\"\n$env:ERPNEXT_COMPANY=\"<company>\"\n$env:ERPNEXT_DEFAULT_EXPENSE_ACCOUNT=\"<expense-account>\"\n$env:ERPNEXT_DEFAULT_COST_CENTER=\"<cost-center>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL. Action Center review links also need `PUBLIC_WEB_APP_URL` to be a public HTTPS URL when reviewers are outside the local browser.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. In live mode, API Workflow tasks call the backend to start extraction, poll review/post/fill state, and record stage callbacks. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## ERPNext Destination\n\nConfigure ERPNext against a sandbox/staging site first. Required values:\n\n- `ERPNEXT_BASE_URL`\n- `ERPNEXT_API_KEY`\n- `ERPNEXT_API_SECRET`\n- `ERPNEXT_COMPANY`\n- `ERPNEXT_DEFAULT_EXPENSE_ACCOUNT`\n- `ERPNEXT_DEFAULT_COST_CENTER`\n\nOptional values:\n\n- `ERPNEXT_DEFAULT_ITEM_CODE` for sites that require an item code on every Purchase Invoice row.\n- `ERPNEXT_PURCHASE_TAXES_AND_CHARGES_TEMPLATE` or `ERPNEXT_VAT_ACCOUNT_HEAD` for tax handling.\n- `ERPNEXT_TEST_SUPPLIER` for live smoke tests.\n\n`ERPNEXT_SUBMIT_AFTER_POST` must stay `false` in v1. ERPNext output is a draft Purchase Invoice plus the uploaded source attachment.\n\nRun live sandbox validation explicitly:\n\n```powershell\n$env:LIVE_ERPNEXT_TEST=\"true\"\n$env:LIVE_UIPATH_TEST=\"true\"\n$env:ERPNEXT_TEST_SUPPLIER=\"<existing-sandbox-supplier>\"\nnpm run smoke:live\n```\n\nThe ERPNext smoke test creates a uniquely numbered `UIPATH-TEST-<timestamp>` draft and leaves it in the sandbox for manual inspection.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDestinations:\n\n```http\nGET /api/destinations\nGET /api/destinations/erpnext/preflight\nPOST /api/jobs/{jobId}/destinations/erpnext/post\nPOST /api/case/jobs/{jobId}/destinations/erpnext/post\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/runtime/config\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n- ERPNext requires existing sandbox master data: Supplier, Company, Account, Cost Center, and tax/item setup as configured.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm run smoke:live\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "e866050d6de711f33b683778dbb9c8e3409a17e4": {"t": 1785772102.0805354, "n": 8592, "h": "dedef65c7f1fa4e8", "c": "# Multi-Platform Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod and ERPNext without IXP or RPA runtime dependency.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to one or more destinations.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n- ERPNext posting is handled through Frappe REST APIs. It creates Purchase Invoice drafts and attaches the source invoice image/PDF; v1 never submits accounting entries.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start -SyncMaestro\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing and used by Action Center review links through `PUBLIC_WEB_APP_URL`.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL and web tunnel URL, then verifies that the running backend and Maestro preflight see those URLs. Tunnel URLs are temporary; restart tunnels and rerun sync before each live Maestro demo.\n\nIf you started tunnels without `-SyncMaestro`, run:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\sync-maestro-tunnel.ps1\n```\n\nRun a live Case callback smoke test:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-tunnel-smoke.ps1 -BatchName \"demo test\"\n```\n\nTunnel rotation does not require redeploying the UiPath package because the current tunnel URL is passed as a Case start input. See `docs/MAESTRO_TUNNEL_ROTATION_FINDINGS.md` for the root cause and recovery details.\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:PUBLIC_WEB_APP_URL=\"http://localhost:5173\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n$env:INVOICE_DESTINATIONS=\"qoyod,erpnext\"\n$env:ERPNEXT_BASE_URL=\"https://your-sandbox.example\"\n$env:ERPNEXT_API_KEY=\"<api-key>\"\n$env:ERPNEXT_API_SECRET=\"<api-secret>\"\n$env:ERPNEXT_COMPANY=\"<company>\"\n$env:ERPNEXT_DEFAULT_EXPENSE_ACCOUNT=\"<expense-account>\"\n$env:ERPNEXT_DEFAULT_COST_CENTER=\"<cost-center>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL. Action Center review links also need `PUBLIC_WEB_APP_URL` to be a public HTTPS URL when reviewers are outside the local browser.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. In live mode, API Workflow tasks call the backend to start extraction, poll review/post/fill state, and record stage callbacks. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## ERPNext Destination\n\nConfigure ERPNext against a sandbox/staging site first. Required values:\n\n- `ERPNEXT_BASE_URL`\n- `ERPNEXT_API_KEY`\n- `ERPNEXT_API_SECRET`\n- `ERPNEXT_COMPANY`\n- `ERPNEXT_DEFAULT_EXPENSE_ACCOUNT`\n- `ERPNEXT_DEFAULT_COST_CENTER`\n\nOptional values:\n\n- `ERPNEXT_DEFAULT_ITEM_CODE` for sites that require an item code on every Purchase Invoice row.\n- `ERPNEXT_PURCHASE_TAXES_AND_CHARGES_TEMPLATE` or `ERPNEXT_VAT_ACCOUNT_HEAD` for tax handling.\n- `ERPNEXT_TEST_SUPPLIER` for live smoke tests.\n\n`ERPNEXT_SUBMIT_AFTER_POST` must stay `false` in v1. ERPNext output is a draft Purchase Invoice plus the uploaded source attachment.\n\nRun live sandbox validation explicitly:\n\n```powershell\n$env:LIVE_ERPNEXT_TEST=\"true\"\n$env:LIVE_UIPATH_TEST=\"true\"\n$env:ERPNEXT_TEST_SUPPLIER=\"<existing-sandbox-supplier>\"\nnpm run smoke:live\n```\n\nThe ERPNext smoke test creates a uniquely numbered `UIPATH-TEST-<timestamp>` draft and leaves it in the sandbox for manual inspection.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDestinations:\n\n```http\nGET /api/destinations\nGET /api/destinations/erpnext/preflight\nPOST /api/jobs/{jobId}/destinations/erpnext/post\nPOST /api/case/jobs/{jobId}/destinations/erpnext/post\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/runtime/config\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n- ERPNext requires existing sandbox master data: Supplier, Company, Account, Cost Center, and tax/item setup as configured.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm run smoke:live\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "e04bc767636bf393ecf63824c719f1eb63fe0a71": {"t": 1785772104.4425316, "n": 8207, "h": "e609188f10459a35", "c": "# Multi-Platform Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod and ERPNext without IXP or RPA runtime dependency.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to one or more destinations.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n- ERPNext posting is handled through Frappe REST APIs. It creates Purchase Invoice drafts and attaches the source invoice image/PDF; v1 never submits accounting entries.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing and used by Action Center review links through `PUBLIC_WEB_APP_URL`.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL and web tunnel URL. Tunnel URLs are temporary; restart tunnels and rerun preflight before each live Maestro demo.\n\nIf Maestro reads `InvoiceIntakeApiBaseUrl` from Orchestrator assets, update that asset after the API tunnel changes:\n\n```powershell\nuip or assets update <asset-id> \"<api-tunnel-url>\" --folder-path \"Finance/InvoiceIntake\" --output json\n```\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:PUBLIC_WEB_APP_URL=\"http://localhost:5173\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n$env:INVOICE_DESTINATIONS=\"qoyod,erpnext\"\n$env:ERPNEXT_BASE_URL=\"https://your-sandbox.example\"\n$env:ERPNEXT_API_KEY=\"<api-key>\"\n$env:ERPNEXT_API_SECRET=\"<api-secret>\"\n$env:ERPNEXT_COMPANY=\"<company>\"\n$env:ERPNEXT_DEFAULT_EXPENSE_ACCOUNT=\"<expense-account>\"\n$env:ERPNEXT_DEFAULT_COST_CENTER=\"<cost-center>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL. Action Center review links also need `PUBLIC_WEB_APP_URL` to be a public HTTPS URL when reviewers are outside the local browser.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. In live mode, API Workflow tasks call the backend to start extraction, poll review/post/fill state, and record stage callbacks. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## ERPNext Destination\n\nConfigure ERPNext against a sandbox/staging site first. Required values:\n\n- `ERPNEXT_BASE_URL`\n- `ERPNEXT_API_KEY`\n- `ERPNEXT_API_SECRET`\n- `ERPNEXT_COMPANY`\n- `ERPNEXT_DEFAULT_EXPENSE_ACCOUNT`\n- `ERPNEXT_DEFAULT_COST_CENTER`\n\nOptional values:\n\n- `ERPNEXT_DEFAULT_ITEM_CODE` for sites that require an item code on every Purchase Invoice row.\n- `ERPNEXT_PURCHASE_TAXES_AND_CHARGES_TEMPLATE` or `ERPNEXT_VAT_ACCOUNT_HEAD` for tax handling.\n- `ERPNEXT_TEST_SUPPLIER` for live smoke tests.\n\n`ERPNEXT_SUBMIT_AFTER_POST` must stay `false` in v1. ERPNext output is a draft Purchase Invoice plus the uploaded source attachment.\n\nRun live sandbox validation explicitly:\n\n```powershell\n$env:LIVE_ERPNEXT_TEST=\"true\"\n$env:LIVE_UIPATH_TEST=\"true\"\n$env:ERPNEXT_TEST_SUPPLIER=\"<existing-sandbox-supplier>\"\nnpm run smoke:live\n```\n\nThe ERPNext smoke test creates a uniquely numbered `UIPATH-TEST-<timestamp>` draft and leaves it in the sandbox for manual inspection.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDestinations:\n\n```http\nGET /api/destinations\nGET /api/destinations/erpnext/preflight\nPOST /api/jobs/{jobId}/destinations/erpnext/post\nPOST /api/case/jobs/{jobId}/destinations/erpnext/post\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n- ERPNext requires existing sandbox master data: Supplier, Company, Account, Cost Center, and tax/item setup as configured.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm run smoke:live\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "b4c477a30a2b61b28910655a74e878203b7e7ebc": {"t": 1785772109.251351, "n": 7909, "h": "f96dacd62c4cc125", "c": "# Multi-Platform Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod and ERPNext without IXP or RPA runtime dependency.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to one or more destinations.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n- ERPNext posting is handled through Frappe REST APIs. It creates Purchase Invoice drafts and attaches the source invoice image/PDF; v1 never submits accounting entries.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL. Tunnel URLs are temporary; restart tunnels and rerun preflight before each live Maestro demo.\n\nIf Maestro reads `InvoiceIntakeApiBaseUrl` from Orchestrator assets, update that asset after the API tunnel changes:\n\n```powershell\nuip or assets update <asset-id> \"<api-tunnel-url>\" --folder-path \"Finance/InvoiceIntake\" --output json\n```\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n$env:INVOICE_DESTINATIONS=\"qoyod,erpnext\"\n$env:ERPNEXT_BASE_URL=\"https://your-sandbox.example\"\n$env:ERPNEXT_API_KEY=\"<api-key>\"\n$env:ERPNEXT_API_SECRET=\"<api-secret>\"\n$env:ERPNEXT_COMPANY=\"<company>\"\n$env:ERPNEXT_DEFAULT_EXPENSE_ACCOUNT=\"<expense-account>\"\n$env:ERPNEXT_DEFAULT_COST_CENTER=\"<cost-center>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages. Once runtime exists, a Case/API Workflow can call the same extraction and callback endpoints.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## ERPNext Destination\n\nConfigure ERPNext against a sandbox/staging site first. Required values:\n\n- `ERPNEXT_BASE_URL`\n- `ERPNEXT_API_KEY`\n- `ERPNEXT_API_SECRET`\n- `ERPNEXT_COMPANY`\n- `ERPNEXT_DEFAULT_EXPENSE_ACCOUNT`\n- `ERPNEXT_DEFAULT_COST_CENTER`\n\nOptional values:\n\n- `ERPNEXT_DEFAULT_ITEM_CODE` for sites that require an item code on every Purchase Invoice row.\n- `ERPNEXT_PURCHASE_TAXES_AND_CHARGES_TEMPLATE` or `ERPNEXT_VAT_ACCOUNT_HEAD` for tax handling.\n- `ERPNEXT_TEST_SUPPLIER` for live smoke tests.\n\n`ERPNEXT_SUBMIT_AFTER_POST` must stay `false` in v1. ERPNext output is a draft Purchase Invoice plus the uploaded source attachment.\n\nRun live sandbox validation explicitly:\n\n```powershell\n$env:LIVE_ERPNEXT_TEST=\"true\"\n$env:LIVE_UIPATH_TEST=\"true\"\n$env:ERPNEXT_TEST_SUPPLIER=\"<existing-sandbox-supplier>\"\nnpm run smoke:live\n```\n\nThe ERPNext smoke test creates a uniquely numbered `UIPATH-TEST-<timestamp>` draft and leaves it in the sandbox for manual inspection.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDestinations:\n\n```http\nGET /api/destinations\nGET /api/destinations/erpnext/preflight\nPOST /api/jobs/{jobId}/destinations/erpnext/post\nPOST /api/case/jobs/{jobId}/destinations/erpnext/post\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n- ERPNext requires existing sandbox master data: Supplier, Company, Account, Cost Center, and tax/item setup as configured.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm run smoke:live\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "08682dd5ee4bc9c942b728569e54518f112da5a7": {"t": 1785772110.4718332, "n": 6081, "h": "d22355bc2aea20cb", "c": "# Qoyod Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod without IXP, RPA, or Qoyod API access.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to Qoyod.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThis closes the public HTTPS tunnels. To stop the local web/API listeners too, stop `npm run dev` with `Ctrl+C` in its terminal.\n\nThe script updates ignored local `.env` with the current API tunnel URL. Tunnel URLs are temporary; restart tunnels and rerun preflight before each live Maestro demo.\n\nIf Maestro reads `InvoiceIntakeApiBaseUrl` from Orchestrator assets, update that asset after the API tunnel changes:\n\n```powershell\nuip or assets update <asset-id> \"<api-tunnel-url>\" --folder-path \"Finance/InvoiceIntake\" --output json\n```\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages. Once runtime exists, a Case/API Workflow can call the same extraction and callback endpoints.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "69fd5d45dd865a5dab723fad58b497a4be304748": {"t": 1785772113.0324643, "n": 5951, "h": "a3824bfe72cdc91b", "c": "# Qoyod Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod without IXP, RPA, or Qoyod API access.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to Qoyod.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Temporary HTTPS Tunnels\n\nUse this when Maestro needs to call the local API or when testing the PWA from a phone without publishing the app.\n\nStart the local app first:\n\n```powershell\nnpm run dev\n```\n\nThen start both HTTPS tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 start\n```\n\nThe script creates:\n\n- API tunnel for `http://localhost:8787`, used by Maestro callbacks and `PUBLIC_API_BASE_URL`.\n- Web tunnel for `http://localhost:5173`, opened on the phone for capture/review testing.\n\nCheck current tunnel URLs:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 status\n```\n\nStop both tunnels:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\dev-tunnels.ps1 stop\n```\n\nThe script updates ignored local `.env` with the current API tunnel URL. Tunnel URLs are temporary; restart tunnels and rerun preflight before each live Maestro demo.\n\nIf Maestro reads `InvoiceIntakeApiBaseUrl` from Orchestrator assets, update that asset after the API tunnel changes:\n\n```powershell\nuip or assets update <asset-id> \"<api-tunnel-url>\" --folder-path \"Finance/InvoiceIntake\" --output json\n```\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages. Once runtime exists, a Case/API Workflow can call the same extraction and callback endpoints.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "3e4dbdcd080946ef33df6c6f47719000dc7aeb85": {"t": 1785772114.4028368, "n": 4783, "h": "23f41e9af897d7e7", "c": "# Qoyod Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod without IXP, RPA, or Qoyod API access.\n\n- Phone PWA captures QR plus one or many invoice photos/PDFs as a batch.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start one Maestro Case per uploaded batch once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA with batch filtering, reusable local mapping rules, and per-invoice release to Qoyod.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. If CaseManagement runtime is not available in your tenant, the PWA shows a local Maestro Case cockpit that mirrors the same stages. Once runtime exists, a Case/API Workflow can call the same extraction and callback endpoints.\n\nRun the read-only preflight before a demo:\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\n```\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nPOST /api/batches\nGET /api/batches\nGET /api/batches/{batchId}\nPOST /api/batches/{batchId}/apply-mappings\nPOST /api/batches/{batchId}/bulk-review\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\nGET /api/mappings\nPOST /api/mappings\nDELETE /api/mappings/{ruleId}\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nMaestro Case callbacks:\n\n```http\nGET /api/case/batches/{batchId}\nPOST /api/case/batches/{batchId}/stage\nPOST /api/case/batches/{batchId}/task\nPOST /api/case/batches/{batchId}/exception\nPOST /api/case/batches/{batchId}/close\nPOST /api/case/jobs/{jobId}/extraction\nPOST /api/case/jobs/{jobId}/review\nPOST /api/case/jobs/{jobId}/exception\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Optionally select a batch scope, then claim, fill, review, and save draft sequentially.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm audit --audit-level=low\npowershell -ExecutionPolicy Bypass -File scripts\\maestro-preflight.ps1\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}, "f351b33659959f85ad8f8e286e453afbfbec9537": {"t": 1785772115.6045127, "n": 3867, "h": "cd02062e8df861da", "c": "# Qoyod Invoice Intake as Backend-First UiPath Maestro Case\n\nTrack 1 pilot for invoice intake into Qoyod without IXP, RPA, or Qoyod API access.\n\n- Phone PWA captures QR plus invoice photo/PDF.\n- Express API stores job state, uploads files to Orchestrator Storage, creates `InvoiceIntake` queue items, and can start a Maestro Case once CaseManagement runtime is available.\n- Extraction runs through a modular backend worker: OpenAI vision/PDF first, DeepSeek JSON normalization second when configured.\n- Finance review remains human-controlled in the PWA or Coded Action App.\n- Qoyod fill is handled by a desktop Chrome extension using the user's logged-in Qoyod browser session. It saves draft only after explicit confirmation.\n\n## Run Locally\n\n```powershell\nnpm install\nnpm run dev\n```\n\nOpen `http://localhost:5173`. The API runs on `http://localhost:8787`.\n\nFor the complete operator walkthrough, see `docs/USER_GUIDE.md`.\n\n## Local Env\n\nCopy `.env.example` and set the local secrets you have:\n\n```powershell\n$env:PUBLIC_API_BASE_URL=\"http://localhost:8787\"\n$env:EXTRACTION_MODE=\"local\"\n$env:OPENAI_API_KEY=\"<openai-key>\"\n$env:OPENAI_EXTRACTION_MODEL=\"gpt-4.1\"\n$env:DEEPSEEK_API_KEY=\"<deepseek-key>\"\n$env:DEEPSEEK_MODEL=\"deepseek-v4-flash\"\n$env:FILLER_API_TOKEN=\"<shared-extension-token>\"\n```\n\nUse `EXTRACTION_MODE=external` plus `EXTRACTION_START_URL` when extraction moves to a separate backend. Cloud Maestro cannot call localhost, so Case-driven execution needs `PUBLIC_API_BASE_URL` to be a public HTTPS URL.\n\n## UiPath Resources\n\nConfigure these per tenant in your local `.env`; no connected environment values are committed.\n\n- Base URL: your UiPath Automation Cloud URL\n- Organization: your organization name\n- Tenant: your tenant name\n- Folder: your target folder path\n- Folder key: your target folder key\n- Queue: `InvoiceIntake`\n- Storage bucket: your invoice-intake storage bucket\n- Case solution: `uipath/QoyodInvoiceIntakeSolution`\n- Case file: `uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json`\n\nThe Case remains the visible Maestro design. If CaseManagement runtime is not available in your tenant, the backend can start extraction immediately after capture. Once runtime exists, a Case/API Workflow can call the same extraction endpoint.\n\n## API Endpoints\n\nCapture and review:\n\n```http\nPOST /api/captures\nGET /api/jobs/{jobId}\nPOST /api/jobs/{jobId}/review\n```\n\nExtraction:\n\n```http\nPOST /api/extraction/jobs/{jobId}/start\nGET /api/extraction/jobs/{jobId}/input\nGET /api/extraction/jobs/{jobId}/source\nPOST /api/extraction/jobs/{jobId}/result\n```\n\nQoyod extension fill:\n\n```http\nPOST /api/fill/jobs/claim-next\nGET /api/fill/jobs/{jobId}\nGET /api/fill/jobs/{jobId}/source\nPOST /api/fill/jobs/{jobId}/status\n```\n\nDeprecated `/api/robot/...` aliases remain for compatibility, but new work should use `/api/fill/...`.\n\n## Chrome Extension\n\nLoad `extension/qoyod-filler` as an unpacked Chrome extension:\n\n1. Open `chrome://extensions`.\n2. Enable Developer mode.\n3. Load unpacked and select `extension/qoyod-filler`.\n4. Log into Qoyod in Chrome and open the draft form.\n5. Click the extension toolbar icon to open the side panel.\n6. Configure API base URL and fill token in the side panel.\n7. Calibrate selectors once, then claim, fill, review, and save draft.\n\nThe extension never stores Qoyod credentials and never clicks approve/submit.\n\n## Current Blockers\n\n- No IXP access: extraction is LLM-backed.\n- No RPA license/runtime: Qoyod fill is extension-assisted.\n- No Qoyod API access: the extension uses the logged-in browser session.\n- No CaseManagement runtime in staging: backend-first execution is active until runtime is allocated.\n\n## Verify\n\n```powershell\nnpm test\nnpm run build\nnpm audit --audit-level=low\nuip maestro case validate uipath/QoyodInvoiceIntakeSolution/QoyodInvoiceIntakeCase/caseplan.json --output json\n```\n"}}}} |