# Deliveries — n8n Signed-Copy Workflow > Spec for the future n8n flow that closes out `/deliveries` items > by uploading the scanned signed invoice/DN to PocketBase and > flipping the order item to `completed`. Read this when you're > ready to wire the workflow — every endpoint, payload shape, and > field is defined below. ## Status: NOT YET BUILT This is a **spec document**, not a working integration. The page `/deliveries` ships with three tabs (To Load / Back from Driver / Checked In); the `checked_in` items sit there until n8n lands. The n8n workflow itself is owned by you, not by the app — this doc just defines the contract so the workflow can be built independently. ## What it does Once the office has typed the reference number on the signed paper (the `checked_in` tab in the app), the signed copy still needs to be: 1. **Scanned** — by whatever scanner / phone-camera / IMAP inbox the office uses today. 2. **Uploaded** to PocketBase on the right `order_items` row. 3. **Status flipped** to `completed` so the office can stop looking at it. The app exposes the PB schema fields for steps 2 + 3; n8n owns step 1. ## API contract (everything the workflow needs) ### Inputs the workflow must know The workflow runs per signed copy, not per order. A single scan might cover 1 line or 20. To find the right `order_items` rows the workflow uses the **reference number** the operator typed at check-in time. The contract is: ``` workflow_in: reference: string // e.g. "INV-12345" or "DN26071401" file: Buffer // the scanned signed copy filename: string // e.g. "INV-12345-signed.pdf" ``` ### 1. Find the order_items Hit the PocketBase list endpoint with the reference filter: ``` GET /api/collections/order_items/records?filter=delivery_reference%20%3D%20%22INV-12345%22&perPage=200 ``` URL-decoded: `filter=delivery_reference = "INV-12345"` > PB's `~` is substring (case-insensitive); `=` is exact. Use > exact — the reference is meant to be exact, and a fuzzy match > risks pulling in the wrong customer's rows. If the workflow > has only a partial reference (e.g. the scanner OCR'd "INV-1234" > with the last digit missing), it should fail loudly rather than > guess. Response shape (each item): ```json { "id": "abc123def456ghi", "delivery_reference": "INV-12345", "checked_in_at": "2026-07-20T11:30:00Z", "checked_in_by": "user_id_here", "item_status": "checked_in" } ``` > ⚠️ Only items with `item_status = "checked_in"` should be > auto-completed. An item that's been typo'd and lives at > `with_driver` or earlier in the lifecycle should be left > alone — the office might re-route it. Optional pre-flight guard: ``` GET /api/collections/order_items/records?filter=delivery_reference%20%3D%20%22INV-12345%22%20%26%26%20item_status%20%3D%20%22checked_in%22 ``` This way the workflow only operates on rows that are genuinely ready to complete. ### 2. Upload the signed copy The signed copy goes onto `order_items.order_item_attachements` (multi-file field, `maxSelect: 10`, accepts any MIME). This is the existing field for "all attachments on this line" — same field that delivery-note PDFs, supplier POs, etc. live on. No new collection, no new field. Per item, do a single PATCH with the file. PocketBase's REST endpoint for file uploads on an existing record is: ``` PATCH /api/collections/order_items/records/{id} Content-Type: multipart/form-data --boundary Content-Disposition: form-data; name="order_item_attachements"; filename="INV-12345-signed.pdf" Content-Type: application/pdf --boundary-- ``` > Naming convention: use the reference number as the filename > stem (`{reference}-signed.pdf` or `{.ext}` as the scanner > produces). The retrieval side — see "How the office sees it > back" below — relies on the filename pattern to find the > signed copy on a given item. Don't append timestamps or random > suffixes. ### 3. Flip the status to `completed` Same record, same endpoint, JSON body this time: ``` PATCH /api/collections/order_items/records/{id} Content-Type: application/json { "item_status": "completed" } ``` No body auth header is needed if the workflow uses the admin PB token (recommended — see "Auth" below). ### 4. Repeat for each item in the batch Loop over the items from step 1 and do steps 2 + 3 per item. A single scan = one batch = N items = N×(PATCH file + PATCH status) calls. PB doesn't have a batch endpoint; the per-item round trip is fine for typical batch sizes (1–20). If the workflow needs to be idempotent (resume after a crash): check `item_status` on the response from step 3. If it's already `completed`, skip. If it's still `checked_in`, the PATCH probably failed — log + retry once. ## Auth The workflow should use the **PocketBase superuser token**, not a per-user token. The PB schema's rule is `@request.auth.id != ''` on the order_items update, so any authenticated principal works, but the superuser path is the simplest because: - It doesn't expire while the workflow is running. - It doesn't need refresh handling. - The signed copy is an automated upload, not a user action — per-user audit doesn't add anything. To grab the token, use the same flow as the rest of the back-office n8n workflows: a single "Bootstrap" HTTP node at the top of the workflow that hits `POST /api/collections/_superusers/auth-with-password` with the admin email + password from the n8n credential store, then stores `token` in a workflow variable. Every subsequent HTTP node reads `Authorization: Bearer {{$token}}`. > If your PB instance is on the v0.23+ rename (admin collection > is `_superusers` not `_admins`), see > `docs/migration-audit.md` for the endpoint confirmation. ## How the office sees it back Once the file is uploaded, the office has two ways to find it: 1. **Inside the existing order detail view** (the one at `/orders/[id]`) — the page already reads `order_item_attachements`. The new file just shows up alongside the others. 2. **Inside the `/deliveries` "Checked In" tab** — when v2 of the page ships (out of scope for v1), it'll show a "View signed copy" link per row that points at PB's file URL. The app's `api/v1/files/[collection]/[recordId]/[filename]` route is the canonical way to stream a PB file back through Next.js with auth. ## Filename contract The workflow must use a deterministic filename per item so the retrieval side can find it without scanning the whole attachment list: ``` {reference}-signed{ext} ``` Examples: - `INV-12345-signed.pdf` - `INV-12345-signed.png` - `DN26071401-signed.pdf` If the scanner produces something else, the workflow should **rename it** to this pattern before the upload — not rely on whatever the device names it. The reference is the only stable key; everything else is mutable. ## Failures to handle The workflow should treat these as "log + retry" rather than "throw and stop the whole run": - **0 items found for the reference** — typo'd reference or a paper that was never typed into the office. Don't create a new item; surface the unmatched reference to a "needs review" sink (a separate `logs` collection or a Slack message — your call). - **HTTP 5xx on the PATCH** — PB blip. Retry once with a 2s backoff. - **`item_status` came back as `cancelled`** — the office cancelled the item in the meantime. Skip the upload + status flip; surface to "needs review". The workflow should treat these as **hard failures** that abort the run: - **More than 50 items for one reference** — almost certainly a data bug. Don't auto-upload 200 files based on a single scan. - **Auth token expired mid-run** — re-bootstrap from the start. ## Suggested n8n shape If you want a starting point: 1. **Trigger**: depends on the scanner. Common patterns: - Watch a dedicated IMAP folder (signed copies get emailed from the scanner). - Watch a folder (e.g. `/shared/scanner/` on a Synology). - A webhook the office hits from a phone app. 2. **OCR / extract reference**: if the scan is an image, OCR it (Tesseract via a `Execute Command` node, or one of the hosted OCR services) to read the reference number from the paper. Cross-check against the manually-typed reference from the office — if they disagree, route to "needs review". 3. **HTTP Bootstrap**: get the PB superuser token. 4. **HTTP Find items**: list by reference (step 1 above). 5. **Loop per item**: upload + flip (steps 2 + 3 above). 6. **Log + cleanup**: move the source file out of the watch folder, log success/failure. ## Out of scope for the app These are deliberately **not** part of the `/deliveries` page v1 and don't need any app change to support: - The actual scan ingestion (n8n). - The OCR (n8n). - The signed-copy viewer in the page (a future "Completed" tab once there's enough data to be worth showing). - A "Completed" tab on the page. When the workflow lands and `item_status = "completed"` rows accumulate, we can add a 4th tab. The schema already supports it — the page just doesn't render it yet.