Description
InvoiceRouter for n8n
v2.1.3 Optional Issuer Diagnostics Hotfix
InvoiceRouter 2.1.3 removes only the IssuerKey/issuer-company hard block introduced in the v2.1.x preflight path. IssuerKey is optional. Missing or different issuer/company evidence is reported as non-blocking Issuer_Compatibility=WARNING; it does not remove accounts from the provider pool and does not block Invoice Sender. Capability, authentication, database, currency, managed-account, lease, duplicate-safety, and send-confirmation gates remain unchanged.
The canonical workflow filename remains n8n-import-workflow-production-v2.1.1.json for backward compatibility. Import the v2.1.3 tagged bytes from:
https://raw.githubusercontent.com/vibtools/n8n-nodes-invoicerouter/v2.1.3/template/providers/odoo/n8n-import-workflow-production-v2.1.1.json
v2.1.2 Corrective Release Synchronization
InvoiceRouter 2.1.2 publishes only the already approved corrective scope: capability-driven Odoo version handling, provider preflight writeback on the original Google Sheets row, safe recovery of pre-provider failed campaign leases, and account-count-agnostic provider loading. The frozen eight-node architecture, public APIs, dependencies, invoice lifecycle, email sending behavior, and canonical workflow filename remain unchanged.
The canonical workflow filename remains n8n-import-workflow-production-v2.1.1.json for backward compatibility. Import the corrected release-tagged bytes from:
https://raw.githubusercontent.com/vibtools/n8n-nodes-invoicerouter/v2.1.2/template/providers/odoo/n8n-import-workflow-production-v2.1.1.json
v2.1.1 Production Corrective Patch
InvoiceRouter 2.1.1 preserves the frozen eight-node architecture and corrects the v2.1.0 production blockers found in live n8n/Odoo execution: embedded Request Builder input handling, version-aware Odoo preflight, truthful no-account queueing, campaign-wide safety, stable Odoo references, ambiguous side-effect reconciliation, ordered Google Sheets writebacks, durable writeback-only repair, and reconciled account/campaign reports.
The canonical URL-importable Odoo workflow is stored at:
template/providers/odoo/n8n-import-workflow-production-v2.1.1.json
After tag v2.1.1, its raw import URL is:
https://raw.githubusercontent.com/vibtools/n8n-nodes-invoicerouter/v2.1.1/template/providers/odoo/n8n-import-workflow-production-v2.1.1.json
Phase 02 durable campaign control
The v2.1.1 production workflow now reconstructs campaign state from emaillist, retryqueue, invoiceresults, and campaignreport; blocks mixed pending CampaignID values; acquires and rereads a campaignreport run lease before the provider loop; tracks a revision and last-attempt timestamp; and releases the lease after the loop. Process memory and workflow static data are caches only. The frozen eight custom nodes and package APIs remain unchanged.
Phase 03 Odoo truthfulness and evidence correction
Current-attempt recipient evidence now has explicit precedence over an ambiguous account.move.send.wizard.actionsendandprint transport result: SENT, then QUEUED, then explicit failure. When the send RPC may have executed but no terminal evidence is available, the outcome is UNVERIFIED, never an automatic retry assumption. Odoo operation metadata remains available in emailevidence.wizardErrorDetails.
Partner resolution uses case-insensitive exact email matching with a two-record ambiguity probe. Duplicate contacts fail closed before invoice creation. Recipient evidence accepts RFC display-name forms such as Name . PDF proof is independently validated by reading ir.attachment and checking application/pdf, account.move, the invoice ID, current-attempt attachment binding, and invoicepdfreport_id.
Phase 04 — Shared Odoo capability, version, and legal-issuer compatibility
InvoiceRouter now uses one canonical manifest, shared/odoo/OdooCapabilityManifest.ts, for the Odoo fields and methods used by Provider Loader preflight, Invoice Sender, and regression tests. Odoo 18 and Odoo 19 retain documented capability profiles. Other server versions are not blocked by a fixed major-version allowlist; they proceed only when the runtime capability probes required by the configured lifecycle pass.
Preflight records the server version, resolved capability profile, authenticated company identity, and the explicit status CAPABILITYVALIDATEDSIDEEFFECTPERMISSION_UNPROVEN. Read-only field/model validation cannot prove create, post, or send permission; those side-effect permissions remain unproven until the controlled live canary.
IssuerKey is optional. Provider Loader reads res.users.companyid and res.company and records issuer/company diagnostics for each FailoverGroup. Missing or different issuer keys and differing company identity are reported as IssuerCompatibility=WARNING; they do not remove accounts from the runtime pool and do not block Invoice Sender.
Do not run two executions for the same Campaign_ID at the same time. The Sheet lease is a fail-closed operational guard, not a transactional database lock.
v2.1.0 Bulk Reliability and Multi-Account Failover
InvoiceRouter 2.1.0 keeps the frozen eight custom nodes and adds one-item just-in-time allocation, stable campaign/job idempotency, side-effect-aware retry, pre-side-effect failover within an Odoo FailoverGroup, real-time emaillist.status, provider account status/counters, durable retry-queue payloads, and account/campaign reporting.
The canonical Odoo template now supports the same simple operator flow for canary and bulk:
Add provider accounts -> add email rows -> connect one workbook -> execute
Use one enabled account and one recipient for account testing. Enable all individually verified accounts for pilot and production bulk. Post/send retries remain locked to the original account after a provider invoice exists.
Before a campaign starts, the canonical workflow performs read-only Odoo authentication, active-currency, and model-access preflight for every enabled account. Managed Google Sheets writes retry three times without returning to the invoice transport path.
See docs/user/odoo-production-bulk-reliability.md and docs/developer/bulk-retry-failover-contract.md.
v2.0.1 Production Email Correctness Hotfix
InvoiceRouter 2.0.1 executes the standard headless Odoo invoice-send wizard, reports email state from provider-side evidence, preserves queued/failed/unverified outcomes, and resumes post/email retries against the existing provider invoice. The frozen eight-node architecture and v2 master workflow topology are unchanged.
v2.0.0 Master Universal Provider Lifecycle
InvoiceRouter is the Vib Tools eight-node n8n community package for guarded provider invoice lifecycles. The master model keeps one package and one frozen custom-node topology while provider adapters execute customer resolution, invoice creation, posting/finalization, email sending, evidence collection, safe retry resume, and status writeback.



InvoiceRouter is an eight-node n8n community-node package for loading many provider accounts from Google Sheets, assigning accounts safely, personalizing invoice data, executing provider requests, standardizing results, and creating retry/metrics/alert/audit events.
Package version: 2.1.3
Architecture: Version 2.0 master lifecycle over the frozen 8-node topology
Implementation: 8/8 custom nodes registered; final publication remains blocked until the complete-project forensic audit passes
v1.6.0 simple bulk email workflow
v1.6.0 keeps the frozen eight-node architecture and makes the default workflow match the intended operator UX:
Google Sheet provider tab = provider/API/secrets/config only
Google Sheet email_list tab = Email required, Name/Address optional
n8n Invoice Template node = invoice details
Trigger execution = one invoice request per recipient row
For Odoo, InvoiceRouter now supports automatic customer handling in the sender path: authenticate from provider-sheet credentials, search res.partner by email, create the partner if missing, then create the invoice. Recipient rows do not require partner_id, database, uid, or password fields. Dry Run performs no Odoo API call.
See 50BUILDINSTALLLIVETESTRUNBOOK.md”>docs/freeze/v1.0/V150BUILDINSTALLLIVETESTRUNBOOK.md for the master build, install, sandbox, live canary, evidence, rollback, and publish checklist.
n8n registry/UI install compatibility
Step 12B hardens the package for npm registry publication and n8n Community Nodes UI installation. The package keeps the npm identity n8n-nodes-invoicerouter@2.1.3, keeps n8n-community-node-package in keywords, removes the install-time n8n-workflow peer dependency risk, and ships a diagnostic script for manual fallback installs.
The n8n editor display names are now prefixed for searchability:
| Search term | Nodes shown |
|—|—|
| InvoiceRouter | all eight custom nodes |
| InvoiceRouter Invoice Sender | sender node |
| InvoiceRouter Request Builder | request builder node |
| InvoiceRouter Provider Loader | provider loader node |
Preferred production install path after npm publish:
n8n Community Nodes UI -> install package: n8n-nodes-invoicerouter
Manual .tgz install remains a fallback only. If used, run the packaged diagnostic from the same n8n runtime/container after installation:
node ~/.n8n/nodes/nodemodules/n8n-nodes-invoicerouter/scripts/diagnose-n8n-package.mjs ~/.n8n/nodes/nodemodules/n8n-nodes-invoicerouter
See InvoiceRouter Version 1 architecture
Manual Trigger
├─> Google Sheets: Provider Accounts/Credentials -> Provider Loader ─┐
├─> Google Sheets: Email List -> Email List -------------------------┼─> Provider Selector
└─> Invoice Template ------------------------------------------------┘Provider Selector allocation ----
Invoice Template -------------------> Request Builder
One normalized recipient ---------/
Request Builder -> Invoice Sender -> Status Checker -> Status Manager
├─ workflow result
├─ retry queue event
├─ metrics/analytics events
├─ alert/audit events
├─ provider feedback/state update
└─ Prepare Status Writeback Row -> Google Sheets: invoice_results
The canonical master workflow is included at:
workflows/InvoiceRouter-v2-master-universal.json
The v1 and v1.6 workflow files remain packaged as compatibility/reference templates and are not removed.
Custom nodes
| # | Node | Responsibility |
|—|—|—|
| 1 | InvoiceRouter Provider Loader | Validates Google Sheets provider rows, creates the frozen providers[] structure, stores runtime secrets in an in-process vault, and masks visible credentials. |
| 2 | InvoiceRouter Provider Selector | Maintains provider state, selects an eligible account, applies rate limits/circuit-breaker rules, and creates lock metadata. |
| 3 | InvoiceRouter Invoice Template | Builds fixed invoice fields, repeatable line items, totals, payment terms, custom fields, and dynamic tag definitions. |
| 4 | InvoiceRouter Email List | Validates recipients, removes duplicate emails, generates missing names, preserves custom columns, and reserves each email once per batch. |
| 5 | InvoiceRouter Request Builder | Merges exactly one account + one template + one recipient, resolves dynamic tags, applies provider presets, validates send readiness, and creates one idempotent ready request. |
| 6 | InvoiceRouter Invoice Sender | Resolves runtime credentials, enforces activation/duplicate/bulk gates, executes provider lifecycle stages, uses the Odoo send wizard for invoice email, records provider evidence, and resumes approved post/email retries against the existing invoice. |
| 7 | InvoiceRouter Status Checker | Analyzes provider and lifecycle evidence, distinguishes success/partial/failure outcomes, classifies errors, and emits a standard status object. |
| 8 | InvoiceRouter Status Manager | Applies retry policy, creates approved lifecycle-resume requests, emits truthful bulk counters and management events, and produces normalized execution-log/writeback payloads. |
Manual Trigger and Google Sheets are built-in n8n nodes and are not part of this package.
n8n node icons and cards
All eight custom InvoiceRouter nodes declare polished SVG runtime icons through n8n’s description.icon field. The build copies each SVG into the matching dist/nodes/ directory so the packaged node card in the n8n editor can resolve its icon beside the compiled node file.
The runtime SVGs are hand-authored vector interpretations of the existing Version 1.0 node-card motif: purple rounded cards, side connector dots, bottom status pills, and node-specific invoice/provider/status symbols. They intentionally avoid text initials so the n8n node list does not depend on font rendering.
The larger PNG files under assets/node-cards/v1.0/ remain repository design/documentation assets and are intentionally not shipped through npm to avoid unnecessary package-size growth. See WRITEBACKWIRING.md”>docs/freeze/v1.0/STATUSWRITEBACKWIRING.md.
Provider request/response mapping hardening
Request Builder now attaches readyRequest.requestMapping and readyRequest.responsePolicy to every prepared request. These metadata blocks document the provider adapter family, canonical action, method/content-type hints, idempotency header hint, success status codes, retryable status codes, non-retryable status codes, and response parsing strategy.
Status Checker supports fallback response path arrays, so provider invoice ID, invoice number, status, URLs, transaction IDs, and error details can be extracted from common response envelopes such as id, invoice.id, or data.id without a second provider API call.
Invoice Sender also blocks live transport if the final interpolated URL, headers, query, or body still contains unresolved template tokens such as {realmId} or {{ACCESS_TOKEN}}. Dry Run remains safe and reports unresolved tokens in rawExecution.requestPreview.unresolvedTokens for review.
The detailed Step 08 contract is documented in DRYRUNVALIDATION.md”>docs/freeze/v1.0/N8NDRYRUNVALIDATION.md. This validation proves import, Sheet reads, conditional routing, guarded blocking, idempotency metadata, execution logging, and status-writeback payload generation. It does not approve a provider sandbox send or a live invoice send.
Processing modes
Provider Selector offers two modes plus optional per-item conditional routing:
Conditional routing
Provider Selector can also route each recipient through conditional routing before it allocates an account. It evaluates optional Routing Rules JSON first, then falls back to per-recipient fields such as:
| Email List column | Runtime path | Purpose |
|—|—|—|
| Provider | recipient.customFields.Provider | Selects the provider profile, for example Stripe or Custom. |
| Action | recipient.customFields.Action | Selects the account/action profile, for example Create Invoice. |
| Environment | recipient.customFields.Environment | Selects sandbox or live. |
When Require Conditional Match is enabled, any recipient without a matching rule or routing fields is returned as BLOCKED; Request Builder and Invoice Sender will not create or send an HTTP request for that item.
Sequential — default
Best for Version 1 and beginners. Multiple recipients may reuse the same account because Invoice Sender processes items in order. This is the default in the bundled workflow.
Parallel locks
Keeps each account locked until Status Manager feedback. Use this only when running controlled parallel workers with enough provider accounts. Unavailable work items are marked QUEUED rather than being sent with the wrong account.
Credential model and security boundary
Version 1 intentionally stores provider API keys/secrets in Google Sheets so many changing accounts can be managed without editing n8n credentials individually.
Provider Loader:
Important boundary: the built-in Google Sheets node receives the original row before Provider Loader runs. In production:
1. Keep the spreadsheet private and use least-privilege Google access.
2. Do not commit real credentials to GitHub.
3. Disable or minimize successful execution-data retention in n8n.
4. Restrict access to workflow executions and logs.
5. Rotate credentials with awareness that Google Sheet revision history may retain previous values.
6. Start with Dry Run enabled and test provider sandboxes before live use.
Install from source
Use Node.js 24 for the supported build environment.
npm ci
npm run verify
npm pack
For a typical self-hosted n8n installation:
mkdir -p ~/.n8n/nodes
cd ~/.n8n/nodes
npm install /absolute/path/n8n-nodes-invoicerouter-1.6.0.tgz
Restart n8n after installation.
Import and configure the workflow
workflows/InvoiceRouter-v1-production.json is a production-shaped template. It is intentionally inactive, uses placeholder spreadsheet and credential IDs, and keeps Invoice Sender → Dry Run enabled.
1. Import workflows/InvoiceRouter-v1-production.json in n8n.
2. Copy the reference workbook into a private Google Sheet.
3. Keep only the provider rows you are actively testing enabled.
4. Replace every demo value required by that provider/action/environment.
5. Open Google Sheets – Provider Accounts and replace the provider spreadsheet ID.
6. Open Google Sheets – Email List and replace the recipient spreadsheet ID.
7. Select your Google Sheets OAuth2 or Service Account credential.
8. Configure Provider Selector deliberately:
– keep environmentFilter explicit; the bundled workflow defaults to sandbox for Dry Run validation, and live must be selected only after sandbox approval;
– either use static providerFilter/actionFilter during single-provider onboarding, or use Conditional Routing with recipient columns/rules;
– the bundled workflow enables Conditional Routing and Require Conditional Match, so missing routes become BLOCKED.
9. Keep Request Builder → Send Guard Mode set to Strict.
10. Keep Request Builder → Idempotency Key Mode set to Provider + Invoice + Recipient unless a reviewed provider-specific reason requires a narrower key.
11. Keep Invoice Sender → Require Send Guard enabled.
12. Keep Invoice Sender → Prevent Duplicate Sends enabled.
13. Keep Status Manager → Respect Provider Retry-After enabled and Retry Max Delay capped for production safety.
14. Keep Invoice Sender → Dry Run enabled.
15. For the first import/run test, use examples/n8ndryrun_validation/ and confirm the expected outcomes in expected-dry-run-outcomes.json.
16. Execute the workflow manually and inspect Provider Loader, Provider Selector, Request Builder, Invoice Sender, Status Checker, and Status Manager output.
17. Configure required provider-specific IDs through custom fields or the Sheet’s Extra Value field.
18. Confirm the invoice ID strategy is stable enough for duplicate prevention.
19. Keep Invoice Sender Activation Safety Mode on dryRunValidation until the n8n dry-run validation package passes.
20. For a real provider sandbox send, set Activation Safety Mode to sandboxRealSend, disable Dry Run, keep routing on sandbox, and enter SENDSANDBOXINVOICES.
21. For a real provider live send, set Activation Safety Mode to liveRealSend, route only live rows, and enter SENDREALINVOICES only after sandbox evidence is accepted.
22. Activate a scheduled/webhook copy only after the manual workflow is stable.
A real invoice-send run is not approved until all setup gates in 10FREEZE.md”>VERSION10FREEZE.md
2. CONTRACTS.md”>docs/freeze/v1.0/NODECONTRACTS.md
4. DECISION.md”>docs/freeze/v1.0/SECURITYDECISION.md
6. DRYRUNVALIDATION.md”>docs/freeze/v1.0/N8NDRYRUNVALIDATION.md
8. REQUESTRESPONSEMAPPING.md”>docs/freeze/v1.0/PROVIDERREQUESTRESPONSEMAPPING.md
10. LIVEACTIVATION.md”>docs/freeze/v1.0/SANDBOXLIVEACTIVATION.md
12. docs/developer/odoo-email-evidence-contract.md.
PDF evidence is a separate identity proof. emailevidence.pdfEvidence.status=VALID requires the current-attempt message to reference the same attachment as invoicepdfreportid, and the attachment must be application/pdf bound to the same account.move invoice. Invalid or unreadable PDF proof is reported without rewriting independently verified mail transport state.
Duplicate-safe lifecycle retry
When a provider invoice already exists, Status Manager can create an approved lifecycle resume request. Post failures resume invoice.post; email failures resume invoice.send_email; both reuse the existing provider invoice checkpoint. UNVERIFIED email outcomes require manual review and are never automatically retried. See docs/developer/lifecycle-retry-resume.md.
Publication and live-test gate
The required order for this release is:
1. Apply all approved delta patches and run npm run verify.
2. Audit the complete final project ZIP, not only the deltas.
3. Correct every audit finding and repeat the forensic audit.
4. Publish only after the full audit passes.
5. Update the package through n8n Community Nodes and verify the loaded package version.
6. Run a one-recipient live canary.
7. Enable live bulk only after Odoo, writeback, PDF, retry-resume, and inbox evidence are accepted.
The release workflow packages the v2 master workflow, compatibility workflows, Odoo mode templates, common status assets, documentation, and the npm tarball into the install bundle.
v2.1.1 Phase 05 exactly-once Sheet envelope
Every recipient has an immutable RowID; provider rows are updated by persisted ProfileID. Before Invoice Sender runs, writebackqueue receives a PROVIDERPENDING operation envelope keyed by Operation_ID. Provider result/checkpoint/evidence update the same envelope and terminal writeback marks it COMPLETE.
v2.1.1 Phase 06 monotonic reporting
campaignreport and accountreport now use monotonic revisions. Every candidate row records BaseRevision, Revision, WriterRunID, and AggregateSource; the workflow rereads the current Sheet row immediately before the write and rejects stale or out-of-sequence writers. The writeback-repair path skips payloads that are already applied or older than the current row and blocks revision gaps.
Campaign totals are rebuilt from durable recipient/result/retry evidence at startup. A prior aggregate row supplies lease and pause metadata, not higher counters that can preserve an obsolete overcount. Account totals resume from the highest-revision durable account row and then add the current event. Odoo issuer/company metadata remains available in provider and account reporting fields. Diagnostic differences use Issuer_Compatibility=WARNING and are not treated as provider failures or sent campaign work.
v2.1.1 Phase 07 final release gate
The final hardening gate pins workflow-engine smoke testing to n8n@2.31.6, exercises restart/other-worker runtime rehydration, validates boundary-aware short-secret redaction, and runs Odoo 18/19 fixture-driven lifecycle tests. Use npm run verify:phase07:static for source prerequisites and npm run verify:phase07:engine for the exact dry-run engine execution.
GitHub/npm publication remains fail-closed on source validation, the Phase 07 static gate, exact n8n@2.31.6 engine smoke, tag/version matching, and npm credential validation. After publication and the n8n Community Nodes update, sanitized, reviewed one-recipient canary and five-recipient/two-account pilot evidence must pass npm run verify:phase07:evidence before production bulk approval. Automated fixtures never claim inbox delivery or live provider approval.
v2.1.1 final corrective forensic gate
The final audit found and corrected four release-critical gaps after the initial Phase 07 implementation:
npm.cmd/npx.cmd directly; it invokes the npm CLI through the active Node executable;PROVIDER_PENDING is now a startup reconciliation input with the exact Request Builder stable reference, preventing blind create/send replay after a crash;Canary and pilot evidence must match the engine evidence hash, engine-tested package tarball hash, and canonical workflow hash. Evidence also records sanitized artifact hashes, reviewer identity/timestamp, immutable RowID/ProfileID checks, issuer-mismatch blocking, and revision behavior. A tag release validates npm credentials before GitHub Release creation.
The automated separate-process test proves provider-pool and vault rehydration from a 66-second resume marker. It is not a substitute for a real n8n database wait/restart/worker test; that proof remains mandatory in the reviewed five-recipient pilot.
Final corrective audit: immutable recipient identity bootstrap
The canonical Odoo workflow reads the virtual Google Sheets rownumber, derives a deterministic RowID, and performs the first identity write with the Google Sheets update operation matched to that exact rownumber. All later recipient status writes match only the persisted immutable RowID. This prevents a blank initial Row_ID from causing appendOrUpdate to append a duplicate recipient row.
Phase 07 live evidence is reproducibly bound to the engine-tested package through deterministic packageContentSha256 and engineBindingSha256 values; timestamp-bearing evidence-file hashes are not used as cross-run identity.
Phase 07 release artifacts also enforce LF text checkout through .gitattributes and LF TypeScript output through compilerOptions.newLine. This keeps deterministic package-content binding reproducible between Windows verification and Linux GitHub Actions.
Phase 07 final evidence additionally verifies every referenced sanitized artifact file beneath evidence/phase07/artifacts/ against its declared SHA-256; arbitrary digest placeholders cannot pass the release gate.