# Purchase Order Ingest — Implementation Notes Cron-driven async queue. Built via n8n MCP on 2026-08-19. ## What it does Every 5 minutes the workflow polls `data_import_batches` for `batch_type=purchase_orders && status=pending` rows, atomically claims them by patching `status=processing && attempts+1`, downloads the original file from PocketBase, runs the AI Agent (MiniMax-M3 + Structured Output Parser) to extract the PO header and line items, and POSTs the structured payload to `/api/purchase-orders/process-batch` on the dashboard. The dashboard route owns the PB inserts and updates the batch row to `processed`/`failed`/`dead`. The previous webhook-based synchronous flow is retired — the browser upload no longer forwards to n8n at all. ## Workflow - **Workflow ID:** `005iI4jX5mq9DsTR` - **URL:** https://wfdash.n8n.apphub.co.za/workflow/005iI4jX5mq9DsTR - **Trigger:** `Every 5 minutes` (Schedule Trigger) - **Status:** published (active version is the cron flow) - **Project:** Personal (David Els ) - **Credentials:** `MiniMax account` is auto-bound to all four `lmChatMinimax` chat-model subnodes (PDF Agent, Image Agent, Parser Model PDF, Parser Model Image). ## Node map ``` Every 5 minutes → Auth PB Admin ├→ Watchdog: List Stuck → Watchdog: Reset Stuck Rows (terminal) └→ Fetch Pending Batches → Loop Over Items ├→ Skip Empty Items → Has ID? → Atomic Claim → Claim Successful? │ ├ (true) → Download File → Route by Content Type → AI Agent / AI Agent1 │ └ (false) → Loop Over Items └──────────────────────────────────────────────────────────────────────────────── AI Agent / AI Agent1 → Build Callback Payload → Send Callback ├ (ok) → Loop Over Items └ (error) → Escalate Failure → Loop Over Items ``` | Node | Type | Purpose | |---|---|---| | Every 5 minutes | scheduleTrigger | Cron tick | | Auth PB Admin | httpRequest (executeOnce) | POST `/api/collections/_superusers/auth-with-password` → returns `{ token, record }`. Downstream nodes reference `$('Auth PB Admin').item.json.token`. | | Watchdog: List Stuck | httpRequest (executeOnce) | GET batches where `status=processing` and `last_attempt_at < 15 min ago` | | Watchdog: Reset Stuck Rows | code (runOnceForAllItems) | PATCH each stuck row back to `pending` with `last_error="watchdog_reset"` | | Fetch Pending Batches | httpRequest (executeOnce) | GET batches where `status=pending` and (`next_retry_at=""` or `next_retry_at