ml-connector
OdooShipStation

Odoo and ShipStation integration

Odoo runs your ERP and warehouse. ShipStation runs your shipping and order fulfillment. Connecting the two keeps inventory synchronized across fulfillment locations, ensures ShipStation knows what Odoo has in stock, and brings shipment status back into Odoo so you know what is shipped and what is still pending. Without the integration, inventory managers reconcile stock counts between systems by hand and shipping staff must re-key tracking numbers into Odoo. With Odoo and ShipStation connected, stock movements and shipments flow automatically.

How Odoo works

Odoo exposes products, inventory levels, purchase orders, customers, and invoices through XML-RPC and JSON-2 REST APIs. Odoo Online and Odoo.sh use subdomain-specific base URLs (https://<subdomain>.odoo.com or https://<subdomain>.odoo.sh), while self-hosted instances use a custom domain. Authentication requires an API key paired with a username login; XML-RPC calls authenticate once per session and receive a uid, while JSON-2 passes the API key as a Bearer token. Odoo's built-in webhooks via Automated Actions are available only in Enterprise with Studio and not production-grade, so inventory and product changes are detected by polling with a high-water-mark timestamp filter on write_date.

How ShipStation works

ShipStation exposes orders, customers, products, shipments, and warehouses through a REST API available in two versions: V1 for order and inventory management, and V2 for label creation and batch operations. Authentication uses an API Key header (V2) or HTTP Basic Auth with base64-encoded key and secret (V1). ShipStation supports outbound webhooks for order and shipment events, but webhook payloads contain only resource pointers (resource_url and resource_type); actual order and shipment data require authenticated follow-up GET requests. Order updates after initial creation require polling, and shipments become immutable once marked as shipped. All V1 datetime fields are in PST/PDT timezone, not UTC.

What moves between them

The primary flow runs bidirectionally. Odoo product catalogs and inventory levels are read on a schedule and pushed to ShipStation warehouses, ensuring ShipStation never offers to ship more than Odoo has in stock. ShipStation shipment and order webhooks are received, dereferenced with authenticated GET calls, and shipment status updates flow back into Odoo fulfillment records. Purchase order receipts in Odoo trigger updates to ShipStation warehouse inventory. Partial shipments are tracked so Odoo shows which orders are fully shipped, partially shipped, or still pending.

How ml-connector handles it

ml-connector stores Odoo API credentials and ShipStation keys encrypted and refreshes authentication tokens as needed. Odoo polls products and inventory on a schedule tied to warehouse cycles, using write_date high-water-mark filtering to find only recent changes and avoid re-processing the same records. Product data from Odoo (SKU, name, weight) is mapped to ShipStation product records. Odoo warehouse locations are mapped to ShipStation warehouse identifiers so inventory levels land on the correct fulfillment center. ShipStation webhooks are received at a registered endpoint, and because the webhook payload is a pointer only (resource_url), ml-connector immediately dereferences it with an authenticated GET request to fetch the full order or shipment data. Timezone conversion is applied to ShipStation dates to align with Odoo's UTC context. ShipStation V1 rate limits (40 requests/min) are respected with exponential backoff and retry. Shipment records that cannot be posted back to Odoo are queued for replay, and every record carries an audit trail.

A real-world example

A mid-sized e-commerce business sells through multiple channels and uses Odoo to manage product catalogs, warehouse inventory, and accounting across two regional distribution centers. Fulfillment is handled by ShipStation, which aggregates orders from the web store, Amazon, and eBay. Before the integration, warehouse staff manually updated inventory in ShipStation when stock was received in Odoo, and the shipping team emailed tracking numbers back to the office to be re-entered into Odoo's order records. Partial shipments created confusion about what was still pending. With Odoo and ShipStation connected, each product receipt in Odoo warehouse updates ShipStation inventory automatically. ShipStation shipment notifications flow back to Odoo, marking orders as fully or partially shipped. The manual inventory reconciliation and tracking number re-entry are gone, and finance can see the true fulfillment status of every order.

What you can do

  • Sync Odoo product catalogs and SKUs to ShipStation, keeping product reference data aligned across both systems.
  • Push Odoo warehouse inventory levels to ShipStation on a schedule, preventing overselling and keeping available stock current.
  • Receive ShipStation shipment and order notifications via webhooks, dereference the data with authenticated API calls, and update Odoo fulfillment records with tracking numbers and status.
  • Map Odoo warehouse locations to ShipStation warehouse identifiers, ensuring inventory and shipments are tied to the correct fulfillment center.
  • Handle API key authentication for both systems, convert timezone-aware dates, respect rate limits, and replay failed records with full audit trail.

Questions

Which direction does data flow between Odoo and ShipStation?
The main flow is bidirectional. Products and inventory levels flow from Odoo to ShipStation, ensuring ShipStation never oversells. Shipment and order status updates flow from ShipStation back to Odoo, keeping fulfillment records current. Purchase order receipts in Odoo update ShipStation warehouse inventory to reflect new stock.
How does the integration handle ShipStation's webhook payload being a pointer rather than full data?
ShipStation webhooks contain only a resource_url and resource_type. ml-connector receives the webhook, immediately makes an authenticated GET request to that resource_url to fetch the full order or shipment data, then processes it. This avoids race conditions where the webhook arrives before the record is fully written.
How does the integration account for ShipStation V1 API rate limits and timezone differences?
ml-connector respects ShipStation V1's 40 requests/minute rate limit with exponential backoff and retry logic. All ShipStation V1 dates are in PST/PDT timezone, so ml-connector converts them to UTC before writing to Odoo to avoid date misalignment in fulfillment records.

Related integrations

Connect Odoo and ShipStation

Free to use. Add your credentials, ping your real systems, and see if we fit.

Get started