# Vendure Connector

> Two-way Vendure integration over the Admin GraphQL API: products, variants, customers, orders, refunds and stock, with a webhook companion.

- **What it is:** Two-way Vendure integration over the Admin GraphQL API: products, variants, customers, orders, refunds and stock, with a webhook companion.
- **For:** eCommerce
- **Odoo versions:** 16.0, 17.0, 18.0, 19.0, 20.0 - Community and Enterprise
- **Runs on:** Odoo.sh or Odoo you host yourself - not Odoo Online, which accepts no third-party apps
- **Price:** 199 EUR, one-time, on the Odoo Apps Store
- **Technical name:** bambooforge_vendure_connector
- **Run for real:** Tested for real against Vendure 3.6 and 3.7: 54 products, 6 customers and 3 orders placed through the Shop API (one paid at 6,480.20) came into Odoo in 60 sync jobs with 0 errors.
- **Not for:** The connector reads products, customers, orders and order slips from your store and writes the corresponding Odoo records. Credentials are stored in admin-only fields.; In scope today: product/customer/order import (products with variants, option groups and assets), product export, order-state mapping, refund discovery from order slips, scheduled sync, signed webhooks.; Out of scope / best-effort: tax auto-matching (off by default, name-based, never auto-creates taxes); multi-warehouse stock routing; subscription/booking order types.
- **Manual:** https://docs.bambooforge.dev/bambooforge_vendure_connector/
- **Live demo:** https://demo20.bambooforge.dev/odoo/action-bambooforge_vendure_connector.action_vendure_control_tower
- **Store page:** https://apps.odoo.com/apps/modules/20.0/bambooforge_vendure_connector
- **Manual last updated:** 2026-10-15

---
# Vendure Connector

A two-way bridge between Odoo 20 and your Vendure store: import products (with
variants), customers and orders, push product changes back, and reconcile refunds
— over the Vendure Admin GraphQL API, with a resilient job queue, dry-run safety,
validation and rollback so you stay in control.

This page is the complete manual. If you follow it top to bottom you can install,
connect, run your first sync, automate it, and fix the common issues **without
contacting support**.

## Matching the storefront total

A storefront charges shipping and grants discounts outside the product lines, so
an order rebuilt from those lines alone is worth less than the order the customer
paid for: the sale looks smaller than it was and never reconciles against the
platform payout.

With **Match Storefront Totals** on the instance (on by default):

- the shipping charge is imported as its own order line, on a clearly named
  service product;
- whatever still separates Odoo from the platform total — platform discounts,
  fees, rounding — is posted as a single adjustment line rather than dropped;
- the platform total and the remaining difference are stored on the order and
  shown on its connector tab;
- an order that still does not match within **Total Tolerance** raises a
  validation warning, so the gap is visible instead of silent.

Imported lines deliberately carry no Odoo tax: the storefront has already
computed tax and it is kept on the order's own tax amount field. Switch the
option off if you would rather Odoo recompute everything from your own price
lists and tax rules.

### Overview

The connector keeps Odoo and Vendure in sync over the official Vendure **Admin
GraphQL API**. Vendure exposes a single GraphQL endpoint (`/admin-api`) rather
than REST resources, so every read and write is a typed GraphQL operation. It
covers:

- **Products** — import Vendure products (with variants, option groups and assets)
  into Odoo product templates (optionally with stock, images and tax class), and
  export Odoo product changes back to the store.
- **Customers** — import Vendure customers (and their addresses) into Odoo contacts.
- **Orders** — import Vendure orders into Odoo sale orders, mapping each order's
  Vendure state to a sale-order action (keep draft, confirm, cancel, or ignore).
- **Refunds** — optionally discover Vendure order slips (credit notes) attached to
  an order and create matching draft customer credit notes in Odoo.

Direction of sync: **Odoo ↔ Vendure** (import is the primary flow; product export
and webhook-driven updates are supported).

Vendure stores money as an integer in the minor unit (cents); the connector
converts it to an Odoo major-unit float on the way in.

Every remote call goes through a queue: nothing is written to Odoo until a job runs,
jobs retry with back-off on transient failures, and a circuit breaker pauses an instance
that keeps failing.

### Tested against the real thing

Automated tests run against a bundled mock store on every release; the line below is the part no mock can give you - the connector pointed at the real platform, with real records and real order totals.

Tested for real against Vendure 3.6 and 3.7: 54 products, 6 customers and 3 orders placed through the Shop API (one paid at 6,480.20) came into Odoo in 60 sync jobs with 0 errors.

### Requirements

- **Odoo**: 20.0, Community or Enterprise.
- **Vendure**: live-tested against Vendure 3.6 over the Admin GraphQL API
  (login / bearer); the 3.x line runs on the same schema, and 2.x is supported —
  validate it on your specific build. Vendure must be reachable from the Odoo server.
- **Python**: no extra libraries beyond a standard Odoo 20 install.
- **Odoo access**: any internal user can use the connector screens. Vendure
  credentials are stored in system-only fields, so only the **Settings /
  Administration** user can read or change the password and bearer token.
- **Network**: outbound HTTP(S) from Odoo to your store. By default the connector
  refuses internal/loopback/private hosts as an SSRF safeguard (see *Safety features*).

### Installation

1. Copy `bambooforge_vendure_connector` into your Odoo addons path.
2. Restart the Odoo service.
3. Open **Apps**, click **Update Apps List**, search for *Vendure*, and press
   **Activate / Install**. Dependencies (*Sales*, *Contacts*, *Invoicing*) install
   automatically.

No Vendure store is required to evaluate the connector: a mock Vendure Admin GraphQL
API ships inside the module, so you can install, explore and run the full import flow
against sample data before pointing it at a real store. Point an instance's **Base
URL** at `<your-odoo>/vendure/mock_api` (auth type *Username + Password*, any
username/password) to use it.

## Webhook companion (optional, for real-time push)

Real-time push from Vendure to Odoo uses a small companion plugin that **ships
inside this module** (it is not sold separately). Without it the connector still
works via the scheduled import crons; with it, changes in Vendure reach Odoo
instantly and signed.

1. Copy `phase2/vendure_plugin/bambooforge-webhook.plugin.ts` into your Vendure
   project at `src/plugins/bambooforge-webhook.plugin.ts`.
2. Register it in `src/vendure-config.ts` by importing
   `BambooForgeWebhookPlugin` and adding it to the `plugins` array.
3. Add to your Vendure `.env`:
   
   ```
   BAMBOOFORGE_WEBHOOK_URL=https://your-odoo.example.com/vendure/webhook
   BAMBOOFORGE_WEBHOOK_SECRET=<the Webhook Secret from the Odoo instance>
   BAMBOOFORGE_INSTANCE_ID=<the vendure.instance id in Odoo>
   ```
4. Restart Vendure.

The plugin subscribes to `ProductEvent`, `CustomerEvent` and
`OrderStateTransitionEvent` on Vendure's EventBus, signs each small JSON body with
HMAC-SHA256 in the `X-Vendure-Signature` header, and POSTs it to the connector
(see *Automation*).

### Step 1 — Create Vendure Admin API credentials

The connector talks to the Vendure **Admin API** as an administrator. You can
authenticate two ways; pick one:

**Option A — Username + Password (login mutation).** Use an existing Vendure
administrator account (for example the `superadmin` you created when seeding
Vendure, or a dedicated admin user). The connector runs the Admin API `login`
mutation with that username and password and reads the bearer token from the
`vendure-auth-token` response header, caching it and re-logging-in when the
session expires.

1. In the Vendure Admin UI, confirm (or create) an administrator with API access.
2. Note its **username/identifier** and **password**.
3. Make sure your Vendure `authOptions.tokenMethod` includes `'bearer'` (the
   default Vendure config does). The connector authenticates with the bearer-token
   method, not cookies.

**Option B — Bearer Token.** If you prefer not to store a password, issue a bearer
token in Vendure and paste it into the connector. The connector then sends that
token directly and skips the login step.

Optional, for real-time updates, you will also install the webhook companion and set
a shared **Webhook Secret** later (see *Installation* and *Automation*).

### Step 2 — Create the connection in Odoo

Open **Vendure Connector ▸ Configuration ▸ Instances** and create a record.

![Vendure instance / connection cockpit](01-connection-cockpit.png)

Key fields:

| Field | What to enter |
|---|---|
| Name | A label for this store, e.g. *My Live Store*. |
| Base URL | Your Vendure server root, e.g. `https://store.example.com` or `http://localhost:9000`. |
| Authentication | *Username + Password (login)* to log in with an admin account, or *Bearer Token* to use a pre-issued token. |
| Username | The Vendure admin identifier (e.g. `superadmin`), used by the login flow. |
| Password | The Vendure admin password (visible to administrators only). |
| Bearer Token | The pre-issued token, for the *Bearer Token* auth type (administrators only). |
| Admin API path | Leave the default `admin-api` unless your Vendure server differs. |
| Verify SSL | Keep on for production. Turn off only for self-signed test certificates. |
| Allow internal host | Off by default. Turn on only to reach a Vendure server on localhost or a private network (lowers the SSRF guard — see *Safety features*). |

Then click **Test Connection**. A green *Connected* state means the credentials and
URL are correct, and the default order-state mapping is seeded automatically. If it
fails, the exact error is shown on the form and recorded in **Logs** (see
*Troubleshooting*).

Tip: use **Quick Setup** (button on the instance) to pick a use case (catalog only,
B2C full sync, …), seed default field mappings and order-state rules, and apply a
recommended schedule in one step.

### Step 3 — First sync (dry-run, then live)

New instances start with **Dry-run** ON. In dry-run, import and delete jobs *simulate*
writes: instead of changing data they produce **Validation Results** you can review under
**Vendure Connector ▸ Operations ▸ Validation Results**. This lets you confirm what
*would* happen before anything is written.

To run a first import:

1. On the instance, click **Import Products** (and/or **Import Customers**,
   **Import Orders**). This enqueues jobs; it does not block the UI.
2. Jobs are processed by the **Vendure Queue Processor** scheduled action (every
   minute), or immediately if you run it manually from **Operations ▸ Queue Jobs**.
3. Review **Validation Results** while still in dry-run.
4. When satisfied, open the instance, turn **Dry-run** OFF, and run the imports again to
   write the records for real.

Imported records land in the standard Odoo apps: **Sales ▸ Products**, **Contacts**, and
**Sales ▸ Orders**. Each carries its Vendure reference so re-imports update the same
record instead of duplicating it.

### Order status mapping

Vendure returns each order's state as a fixed PascalCase string (`Draft`,
`AddingItems`, `ArrangingPayment`, `PaymentSettled`, `Shipped`,
`Delivered`, `Cancelled`, …). Shops may add custom states via the OrderProcess.
Under **Configuration ▸ Order State Mappings** each instance gets a default table
(seeded on first successful connection) that decides what happens to the Odoo sale
order when an order arrives in a given state:

| Vendure state | Default Odoo action |
|---|---|
| Draft | Keep draft |
| AddingItems | Keep draft |
| ArrangingPayment | Keep draft |
| PaymentAuthorized | Confirm sale order |
| PaymentSettled | Confirm sale order |
| PartiallyShipped | Confirm sale order |
| Shipped | Confirm sale order |
| PartiallyDelivered | Confirm sale order |
| Delivered | Confirm sale order |
| Modifying | Keep draft |
| ArrangingAdditionalPayment | Keep draft |
| Cancelled | Cancel sale order |

Change any row to **Ignore (do nothing)**, **Keep draft**, **Confirm sale order** or
**Cancel sale order**. Unknown/custom states default to *Ignore*, so a state you have
not mapped never triggers a destructive transition. Add a row for any custom state your
OrderProcess introduces (use the exact PascalCase state string).

### Field mapping & customization

- **Field Mappings** (Configuration) map Vendure fields to Odoo fields per model.
  Use **Generate suggested mappings** on the instance to seed the business-critical ones,
  then adjust. The mapping board flags fields that need attention.
- **Schema Fields** lists the discovered Vendure fields per resource. Run **Schema
  Introspection** on the instance to refresh it from your live store.
- **Stock / images / tax** are opt-in toggles on the instance: *Sync stock*, *Sync
  images* (and *max images*), *Upload images on export*, *Sync taxes* (and *auto-match
  taxes on import*), *Sync refunds* (and *auto-post refund*). They are off by
  default; turn on only what you need.

### Automation (scheduled actions & webhooks)

The module ships these scheduled actions (**Settings ▸ Technical ▸ Scheduled Actions**):

| Scheduled action | Default | Purpose |
|---|---|---|
| Vendure Queue Processor | every 1 min | Processes queued import/export/delete jobs. |
| Vendure Reconciliation | every 15 min | Pulls recent remote changes for enabled models. |
| Vendure Maintenance | every 1 hr | Recovers stale/locked jobs and trims old logs. |
| Vendure Flow Scheduler | every 5 min | Runs scheduled sync flows. |
| Vendure Flow Metrics | every 1 hr | Aggregates flow-run metrics. |
| Vendure Auto Recover | every 15 min | Reopens a tripped circuit breaker once the store is healthy. |

Turn on **Auto import** / **Auto reconcile** per model on the instance to let the
scheduled actions keep things in sync hands-free.

**Real-time webhooks** (optional): install the webhook companion (see *Installation*).
It POSTs signed events to:

- **Delivery URL**: `https://<your-odoo-domain>/vendure/webhook`
- **Secret**: the instance **Webhook Secret** (admin-only field on the instance) — set
  the same value in the plugin's `BAMBOOFORGE_WEBHOOK_SECRET`.

Odoo verifies the HMAC-SHA256 signature (`X-Vendure-Signature` header) on each
delivery, rejects stale or replayed deliveries, and enqueues a safe import. Without
the companion the scheduled reconciliation still keeps data current.

### Safety features

- **Dry-run mode** — simulate writes and review Validation Results before going live.
- **Business validation profiles** — *Minimal / Standard / Strict* gate risky writes.
- **Resilient queue** — every remote action is a job with retry and exponential back-off.
- **Circuit breaker** — after repeated failures an instance auto-pauses (*Tripped*); the
  Auto Recover action reopens it once the store responds again.
- **Rollback snapshots** — when enabled, imports capture a snapshot so you can undo a
  batch from **Operations ▸ Rollback Snapshots**.
- **SSRF guard** — the connector refuses internal/loopback/private hosts unless *Allow
  internal host* is explicitly enabled.

### Where Vendure shows up in Odoo

The connector stays out of the way on records that have nothing to do
with Vendure:

- **Sales orders** - the *Vendure* tab, *Queue Vendure Export* and *Vendure Binding*
  appear only on orders that came from Vendure, are bound to Vendure, or belong
  to a customer bound to Vendure.
- **Products and contacts** - the *Vendure* tab appears once an active Vendure
  connection exists; the *Vendure Binding* button inside it appears once the
  record is bound.
- List views, the sync queue and the control tower are unchanged.

### Troubleshooting

| Symptom | Cause and fix |
|---|---|
| Test Connection fails with an auth error | Wrong username/password or bearer token, or the account lacks Admin API access. Re-check Step 1. |
| Login succeeds elsewhere but not here | Vendure `tokenMethod` does not include `'bearer'`. Enable the bearer token method in `authOptions`, or switch to the *Bearer Token* auth type. |
| "Refused internal/private host" | Base URL points at localhost/private IP. Enable *Allow internal host* on the instance (test/self-hosted only). |
| SSL errors on Test Connection | Self-signed certificate. Use a valid cert, or turn off *Verify SSL* for testing only. |
| GraphQL endpoint not found (404) | The **Admin API path** is wrong. Leave it at `admin-api` unless your Vendure server is configured differently. |
| Orders import but never confirm | The order's Vendure state is mapped to *Keep draft* or *Ignore*. Adjust **Order State Mappings**. |
| Jobs stay in *Pending* | The Queue Processor is off or the instance is paused. Check **Scheduled Actions** is active and the instance is not *Paused*. |
| Instance shows *Tripped* | Circuit breaker tripped after repeated failures. Fix the store/credentials; Auto Recover reopens it, or click **Resume**. |
| Prices look 100x too big or small | Vendure money is an integer minor unit (cents); the connector converts it to a float. If a custom mapping overrides the amount field, keep the built-in money conversion in place. |
| Webhook returns 401 / 409 | 401 = signature mismatch (the plugin secret and the instance **Webhook Secret** differ); 409 = duplicate/replayed delivery (already processed). Align the secret. |
| Records imported twice | Imports are keyed by the Vendure reference, so this should not happen. If it does, check that two instances do not point at the same store. |
| Nothing happens after Import | You are in **Dry-run**. Review Validation Results, then turn dry-run off and re-run. |

For anything else, **Operations ▸ Logs** records every API call, payload hash and error
with a timestamp.

### Frequently asked questions

**Which versions are supported?**
Odoo 20.0 on the Odoo side. On the store side the connector was live-tested on
Vendure 3.6 over the Admin GraphQL API (login / bearer) and runs on the 3.x line on
the same schema; 2.x is supported — validate it on your specific build. Validate your
exact build with the bundled mock first.

**Do I need Vendure installed to evaluate it?**
No. A mock Vendure Admin GraphQL API ships inside, so you can install, explore and demo
the full import flow before connecting a real store. Point the Base URL at
`<your-odoo>/vendure/mock_api`.

**How does authentication work?**
Two ways. *Username + Password* runs the Admin API `login` mutation and reads the
bearer token from the `vendure-auth-token` header (your Vendure `tokenMethod` must
include `'bearer'`, which the default config does). *Bearer Token* sends a pre-issued
token directly. Both are built in; passwords and tokens are admin-only fields.

**How does real-time sync work?**
Install the bundled Vendure webhook companion (see *Installation*). It subscribes to
product, customer and order-transition events, signs each with HMAC-SHA256, and POSTs
to `/vendure/webhook`; Odoo verifies the signature, rejects replays, and enqueues a
safe import. Without it, scheduled reconciliation keeps things in sync.

**Is it safe to run against production data?**
Dry-run is ON by default, validation blocks risky writes, and rollback snapshots let you
undo. You decide when to go live.

**What support and refund policy do I get?**
Every request is answered within 24 hours, setup help included. If you report a bug
within 2 months of purchase and it is not resolved within 15 days, you are entitled to a
full money-back refund.

### Data, privacy & limits

- The connector reads products, customers, orders and order slips from your store and
  writes the corresponding Odoo records. Credentials are stored in admin-only fields.
- **In scope today**: product/customer/order import (products with variants, option
  groups and assets), product export, order-state mapping, refund discovery from order
  slips, scheduled sync, signed webhooks.
- **Out of scope / best-effort**: tax auto-matching (off by default, name-based, never
  auto-creates taxes); multi-warehouse stock routing; subscription/booking order types.

### Support & updates

- Support: [support@bambooforge.dev](mailto:support@bambooforge.dev) — answered within 24 hours, setup help included.
- Refund: report a bug within 2 months of purchase; if unresolved within 15 days, full
  refund.
- Full source is included. Updates track the supported Odoo 20 / Vendure Admin GraphQL line.

### Upgrading & version compatibility

This build targets **Odoo 20.0**. Each Odoo major series (16.0, 17.0, 18.0, 19.0, 20.0) has its
own dedicated build of this module — always install the build that matches your Odoo
version. Mixing a build with a different Odoo series is not supported.

## Patch upgrades (same series, e.g. 20.0.1.0.0 → later)

1. **Back up** your database and filestore first.
2. Replace the module folder with the newer build.
3. Restart Odoo with the module updated:
   
   ```
   ./odoo-bin -c your.conf -u bambooforge_vendure_connector -d your_db
   ```
4. Odoo applies any schema/data changes automatically. Your existing records and
   configuration are preserved.

## Cross-version migration (moving to a newer Odoo)

Upgrading Odoo itself is a database migration handled by Odoo's standard upgrade
tooling. When you migrate the database to the next Odoo series, install the matching
build of this module for that series. Data created by this module carries over with
the database migration.

After any upgrade the module's scheduled actions resume on their normal cadence —
no manual re-activation is required.

### Uninstallation

You can remove this module at any time from **Apps → (this module) → Uninstall**,
or from the command line. Uninstalling is clean and reversible by reinstalling —
but note what is and is not deleted.

## What is removed

- The module's own tables and every record in them (20 models, prefixed `vendure.*`) — this is the data this module created.
- The menus, actions, views and reports this module installed.
- Its scheduled actions (cron jobs) — they stop immediately on uninstall.
- Connection records, credentials, field mappings, queue jobs and sync logs stored in Odoo.

## What is preserved

- **Your remote platform is never touched.** Uninstalling only removes the Odoo-side
  connector; products, customers and orders on the external store/service are untouched.
- Records already imported into standard Odoo models (e.g. contacts, products, sales
  orders) remain — they are ordinary Odoo records once created.
- Attachments and chatter messages on standard records are kept.

As always, take a database backup before uninstalling in production.

### Changelog

## 20.0.1.2.4

- Built for Odoo 20: record rules and access rights rewritten onto Odoo 20's single ir.access model, the screens rebuilt on Owl 3, and every icon moved to the icon set Odoo 20 ships.

## 20.0.1.0.0

Current release for Odoo 20.0. This build includes:

- Two-way Odoo 20 <-> Vendure connector: products, variants, customers, orders and refunds over the Vendure Admin GraphQL API.
- Two-way stock sync, live Sync Control Tower, webhook companion, resilient queue and dry-run safety.

Feature additions and fixes ship as new builds on the Odoo Apps store; this page and
the module's `version` reflect the current published release. Always keep the build
matched to your Odoo series (see *Upgrading & version compatibility*).

### How we make sure it works

Before every release, **95 automated checks** run against this app on Odoo 16, 17, 18, 19 and 20.
Among them:

- Nothing at all is an empty row not a crash.
- A product shows its binding button only once bound.
- Map refund to credit note.
- Import to odoo true but dry run never moves stock.
- The queue only calls mapper methods that exist.

Tested for real against Vendure 3.6 and 3.7: 54 products, 6 customers and 3 orders placed through the Shop API (one paid at 6,480.20) came into Odoo in 60 sync jobs with 0 errors.

The complete list is in the source you receive.
