# Quote Outbound Send — operator-uploaded attachments fix Deployed 2026-09-09 alongside the `quotes.outbound_attachments` / `quotes.internal_attachments` storage split (the migration that moved operator-uploaded files off the legacy `quote_attachments` collection's `direction` enum). ## What the deployed workflow does now The active n8n workflow is `WFDASH — Quote Outbound Send (push-payload + outbound-attachments)` (id `PIEAkPYYYZLGl6Pj`, previously named `…(push-payload)`). The chain still ends in `Send Email (SMTP)` with the auto-generated PDF attached — same as before — **plus** a new `Build attachments list` Code node that runs between `HTTP Request` (PDF download) and the SMTP node. It: 1. Reads `quotes.outbound_attachments` (the PB file field on the parent quote row) and `email_outbound_attachment_ids` (the JSON array of operator-selected tokens persisted by `sendQuoteEmail`). 2. Intersects the two so only files the operator explicitly marked for the customer get attached. Defends against drift between the modal snapshot and the send. 3. Downloads each token from `https:///api/files/quotes//` via `this.helpers.httpRequest`. 4. Returns a single item with `attachments: [{name, content (base64), type}, …]` on the JSON — same shape the deployed `WFDASH — Purchase Order Send` workflow consumes. 5. Per-download failures are skipped (not raised) so a single bad token doesn't abort the whole send. Operator sees the final count via `attachmentCount`. The SMTP node now consumes the array via `options.attachments={{ $json.attachments && $json.attachments.length ? $json.attachments : [] }}` — matching the PO Send pattern. The previous `options.fileAttachments=data` (PDF only) is removed. ## Bonus fix: failure status `send_failed` → `mail_failed` The deployed `Update quote - Send Failed` node previously flipped a failed send to status `send_failed`. The dashboard's `retryQuoteEmail` only accepts `mail_failed`, so failed sends were stranded — operator couldn't retry from the UI. The failure path now flips to `mail_failed`. Stale `send_failed` rows (if any pre-existed) can be re-categorised with a one-shot PB update if the operator wants the tab badge to count them. ## Why the repo file was deleted `n8n_workfows/WFDASH — Quote Outbound Send.json` (the pre-existing repo file) was **stale** — it referenced a non-existent MCP tool (`quotes.get_send_payload`), the old row-ID model (`selected_attachment_ids` + `pdf_attachment_id`), `$env.PB_ADMIN_TOKEN` (which the docker-compose blocks via `N8N_BLOCK_ENV_ACCESS_IN_NODE=true`), and field names that had been renamed (`email_message_id`, `email_delivered`, …). It did **not** match the deployed workflow and would have misled anyone debugging the system. Deleted. A clean snapshot of the previously-deployed workflow is preserved at `n8n_workfows/WFDASH — Quote Outbound Send (push-payload).baseline.json` for diff/rollback reference. The post-fix deployed workflow is in `n8n_workfows/WFDASH — Quote Outbound Send (with-outbound-attachments).json`. ## MCP `quotes.send_email` description Updated to spell out the new field semantics: "`selectedAttachmentIds` is a list of PB file tokens from `quotes.outbound_attachments` (operator-uploaded customer-facing files — supplier quotes, drawings, NDAs). The PDF is always attached separately from `quote_attachments` kind="pdf"`. Also added a per-property description on the input schema and updated the `docs/api-mcp-layer.md` table to match. ## Verification | Layer | Result | |---|---| | Deployed workflow via n8n REST | PUT returned HTTP 200, `active=true`, node count 24 → 25, new connections verified | | `quotes-service.test.ts` | 117 pass, 4 pre-existing failures (stale-PDF auto-regen — unrelated, baseline fails too) | | `mcp/route.test.ts` | 118 pass, 2 pre-existing failures (unrelated, baseline fails too) | | Code node JS syntax | `node --check` passes | ## Files touched - **Deleted**: `n8n_workfows/WFDASH — Quote Outbound Send.json` - **Added**: - `n8n_workfows/WFDASH — Quote Outbound Send (push-payload).baseline.json` - `n8n_workfows/WFDASH — Quote Outbound Send (with-outbound-attachments).json` - `docs/quotes-outbound-attachments-deploy.md` - **Modified**: - `src/app/api/mcp/route.ts` (tool description + `selectedAttachmentIds` schema property) - `src/app/api/v1/quotes/[id]/send-email/route.ts` (docstring only) - `docs/api-mcp-layer.md` (table row) - **Deployed**: workflow `PIEAkPYYYZLGl6Pj` in the live n8n instance