← All posts
June 10, 2026 · Product

We built SideQuest for Odoo. Here's the honest state. Beta

For the last several days the bulk of our build work has been a sister product: SideQuest for Odoo. The same five-step matcher, the same draft-for-review gate, the same local-first data posture as the QuickBooks Online connector — but talking XML-RPC to a sale.order instead of REST to a QuickBooks Estimate. Phases 1 through 4 are built and validated live against an Odoo 17 sandbox using a real Gmail inbox. This post is a straight accounting of what works, what doesn't, and the architecture calls that mattered.

Status: Private beta. The connector runs end-to-end. The release gate is still closed — we require an external tester signoff and an internal one before opening signup. One tester is going first; the waitlist opens after. Email [email protected] with subject "Odoo beta interest" to join the next batch.

Why Odoo

The QBO connector solved a specific shape of problem: a distributor on QuickBooks Online getting POs by email, with someone retyping every line into Estimates. That same shape sits on top of Odoo. Different ERP, same painful workflow, different ecosystem of failed fixes around it (Conexiom, Rossum, Odoo dev shops). Distributors on Odoo asked us if we'd come over. The answer was yes if the architecture could be reused without forking.

The architecture reused cleanly. The matcher, parser, draft store, cross-reference flywheel, insights engines, and dashboard renderer are pure data-in/data-out modules with no QuickBooks coupling. Odoo just needed a different client at the bottom of the stack — a OdooClient instead of a QBClient — speaking XML-RPC with a per-user API key, returning the same shape of records.

What's live

Seven phases are validated against a live Odoo 17 sandbox running in Docker, with a real Gmail token minted via OAuth. 66 unit tests green, five live integration smoke suites green (the most recent: Phase 7 operator-productivity tools live-tested against real drafts — review-queue surfaced our dirty $283K Contractor PO from earlier testing, price-variance correctly flagged a po_higher line, and the bulk-submit safety contract held):

The four architecture calls that mattered most

1. Vendor, don't extract — at least until the gate is open

The shared core lives in the QBO connector as canonical and is copied verbatim into the Odoo project, with a drift ledger and a discipline rule: any change to a shared file in QBO has to be re-vendored to Odoo at the same version, or the divergence is logged and explained. The alternative was extracting a sidequest_core package both products import. We chose vendoring because pre-launch, the cost of accidental breakage in QBO (which has paying customers) outweighs the cost of duplication. Extraction is queued for after Odoo exits beta.

2. Local-first stays local-first

The privacy posture from QBO carries over without compromise. PO contents, customer pricing, the Odoo catalog, and the learned cross-references all live on the operator's machine. The only thing reported back to our control plane is an anonymous monthly PO count for usage metering. As of Phase 6 that wiring is live — but the control plane runs Odoo licenses on a separate product tier from QBO, so beta-test traffic shows up in its own admin view and never contaminates the QBO numbers our paying customers drive.

3. Build the XML-RPC client inside try blocks

Odoo's XML-RPC surface throws xmlrpc.client.Fault with a 30-line Python traceback embedded in the fault string. The first version of OdooClient let those propagate. The second wraps every call in a try block, catches the Fault, parses the underlying error class out of the traceback, and emits the same typed error envelope the QBO connector uses: {"error": "odoo_unavailable", "exception_type": "OdooAuthFailed", "message": "...", "hint": "..."}. The CLI and MCP tools never see a raw Python exception. The diagnose prompt linking to /diagnose works the same way it does for QBO.

4. Don't trust Odoo's button methods over XML-RPC

Phase 2's first live test failed in an instructive way: action_quotation_sent() raised a Fault over XML-RPC even though the state change committed. Odoo's button methods fire mail-tracking side effects and return non-serializable action dicts that don't marshal cleanly through XML-RPC. The fix was to set state via a plain write({'state': 'sent'}) — deterministic, side-effect-free, exactly what a programmatic submit wants. We do not call action_confirm() or action_cancel() either. Both gotchas are documented in the project's FINDINGS log so the next person doesn't relearn them.

What's still beta

Honestly: a lot. The connector works end-to-end, but the operator polish that makes it feel like a finished product still has gaps:

The release gate exists to keep us honest about that last point. No paying customer touches this until an external tester signs off and we sign off. We're not skipping that.

What we're optimizing for

The same thing as on QBO: the smallest tool that closes the inbound-PO loop without forcing the customer off their ERP, without a 6-week implementation, and without their PO contents leaving their machine. Distributors don't need another platform — they need their typing to stop. The job hasn't changed because the ERP changed underneath it.

Want to be in the next batch? One external tester is going first; you'd be in the second wave. Email us your sell vertical, monthly PO volume, and Odoo version.
Email [email protected]