This article is currently available in English only. Translation coming soon.
By the end of this recipe, your Amazon seller account will sync orders, inventory, and product listings to Odoo 19 via the new Selling Partner API (SP-API), with FBA inventory reconciled nightly, MFN orders generating immediate Odoo SOs, and Amazon-charged fees posted as the right vendor bills against your accounting books. Skill required: senior Odoo developer with REST API + AWS Signature V4 experience. Time required: 8 hours setup, 4 hours testing across MFN and FBA flows. ECOSIRE has built this for Amazon sellers grossing $5M to $50M/year, and the recipe below is the playbook we ship.
The core complication: SP-API replaced MWS (Marketplace Web Service) in 2024, and the migration broke every Odoo connector that targeted MWS. SP-API uses OAuth 2.0 + AWS Sigv4 signing for every request — significantly more setup than MWS but more reliable once configured. The recipe below targets SP-API only.
What you will need
- Amazon Seller Central account with Professional seller plan ($39.99/month US).
- SP-API Developer registration at
developer.amazon.com(free). - AWS account for the IAM user that signs SP-API requests.
- LWA (Login with Amazon) app: created in Seller Central > Develop Apps.
- Odoo version: 17, 18, or 19. The OCA
connector_amazonmodule needs forking for SP-API; we typically build a thin custom connector instead. - Time: 8 hours setup, 4 hours testing.
- Skill: REST APIs, AWS Sigv4 signing, OAuth 2.0 token refresh.
Step-by-step
1. Register as an SP-API developer
In Seller Central > Apps and Services > Develop Apps > Add new app client. Set "API Type" = "SP-API". Pick the data scopes you need: Orders, Inventory, Listings, Reports. Save the LWA Client ID and LWA Client Secret.
In a separate tab, create an IAM user in AWS with policy AWSSellingPartnerAPIRole attached. Save the access key ID + secret access key. Verification: app status shows "Active" in Seller Central.
2. Authorize the app for your seller
Go to the app's "Authorize" tab. Click the magic link, log in to your Amazon seller account, and approve. You receive a refresh_token valid for ~1 year. Paste it into Odoo config:
class AmazonBackend(models.Model):
_name = 'amazon.backend'
name = fields.Char(required=True)
lwa_client_id = fields.Char()
lwa_client_secret = fields.Char()
refresh_token = fields.Char()
aws_access_key_id = fields.Char()
aws_secret_access_key = fields.Char()
region = fields.Selection([('us-east-1', 'NA'), ('eu-west-1', 'EU'), ('us-west-2', 'FE')])
marketplace_ids = fields.Char(default='ATVPDKIKX0DER') # ATVPDKIKX0DER = US
Verification: the backend record saves and you can manually trigger token refresh.
3. Implement OAuth token exchange
SP-API requires an access_token (1-hour lifetime) for every request. Refresh from the long-lived refresh_token:
import requests
def _get_access_token(self):
cache_key = f'amazon.access_token.{self.id}'
cached = self.env['ir.config_parameter'].sudo().get_param(cache_key)
if cached:
token, expiry = cached.split('|')
if datetime.now().timestamp() < float(expiry) - 60:
return token
resp = requests.post('https://api.amazon.com/auth/o2/token', data={
'grant_type': 'refresh_token',
'refresh_token': self.refresh_token,
'client_id': self.lwa_client_id,
'client_secret': self.lwa_client_secret,
}, timeout=30)
resp.raise_for_status()
data = resp.json()
expiry = datetime.now().timestamp() + data['expires_in']
self.env['ir.config_parameter'].sudo().set_param(
cache_key, f"{data['access_token']}|{expiry}"
)
return data['access_token']
Verification: the function returns a non-empty token; calling it 5 times in a row returns the same cached token until expiry.
4. Sign requests with AWS Sigv4
SP-API requires AWS Sigv4 signing in addition to the LWA token. Use the official requests-aws4auth library:
from requests_aws4auth import AWS4Auth
def _signed_request(self, method, path, params=None, json=None):
token = self._get_access_token()
auth = AWS4Auth(
self.aws_access_key_id,
self.aws_secret_access_key,
self.region,
'execute-api',
)
base_url = {
'us-east-1': 'https://sellingpartnerapi-na.amazon.com',
'eu-west-1': 'https://sellingpartnerapi-eu.amazon.com',
'us-west-2': 'https://sellingpartnerapi-fe.amazon.com',
}[self.region]
headers = {'x-amz-access-token': token}
return requests.request(method, base_url + path,
params=params, json=json,
headers=headers, auth=auth, timeout=60)
Verification: a GET /sellers/v1/marketplaceParticipations returns a 200 with the list of marketplaces you sell on.
5. Pull orders
SP-API's Orders endpoint paginates by NextToken. Pull last 24 hours every 15 minutes:
def _pull_recent_orders(self):
cutoff = (datetime.utcnow() - timedelta(hours=24)).isoformat() + 'Z'
next_token = None
while True:
params = {
'MarketplaceIds': self.marketplace_ids,
'CreatedAfter': cutoff,
}
if next_token:
params['NextToken'] = next_token
resp = self._signed_request('GET', '/orders/v0/orders', params=params)
resp.raise_for_status()
data = resp.json()['payload']
for order in data.get('Orders', []):
self._upsert_amazon_order(order)
next_token = data.get('NextToken')
if not next_token:
break
For each order, fetch line items separately via GET /orders/v0/orders/{orderId}/orderItems, then create the Odoo SO:
def _upsert_amazon_order(self, order_data):
SO = self.env['sale.order']
existing = SO.search([('amazon_order_id', '=', order_data['AmazonOrderId'])])
if existing:
return existing
partner = self._get_or_create_amazon_partner(order_data)
items_resp = self._signed_request('GET', f"/orders/v0/orders/{order_data['AmazonOrderId']}/orderItems")
line_data = items_resp.json()['payload'].get('OrderItems', [])
so = SO.create({
'partner_id': partner.id,
'amazon_order_id': order_data['AmazonOrderId'],
'amazon_marketplace': order_data['MarketplaceId'],
'date_order': order_data['PurchaseDate'],
'order_line': [(0, 0, {
'product_id': self._get_product_by_sku(item['SellerSKU']).id,
'product_uom_qty': item['QuantityOrdered'],
'price_unit': float(item['ItemPrice']['Amount']),
}) for item in line_data],
})
so.action_confirm()
return so
Verification: place a real Amazon order on a test SKU, run the pull, and the SO appears in Odoo within 15 minutes.
6. Reconcile FBA inventory
Run a daily cron to pull FBA inventory levels and update product.product.qty_available on the FBA stock location:
def _cron_pull_fba_inventory(self):
resp = self._signed_request('GET', '/fba/inventory/v1/summaries', params={
'granularityType': 'Marketplace',
'granularityId': self.marketplace_ids,
'marketplaceIds': self.marketplace_ids,
})
fba_loc = self.env.ref('stock.stock_location_amazon_fba')
for summary in resp.json()['payload']['inventorySummaries']:
product = self.env['product.product'].search([('default_code', '=', summary['sellerSku'])], limit=1)
if not product:
continue
qty = summary['inventoryDetails']['fulfillableQuantity']
# Adjust the on-hand quantity for the FBA location
self.env['stock.quant']._update_available_quantity(product, fba_loc, qty)
Verification: FBA-warehouse on-hand in Odoo matches the Seller Central FBA dashboard within 1 day.
7. Pull settlement reports for accounting
Amazon settles every 2 weeks with a settlement report. Pull it via POST /reports/2021-06-30/reports with reportType=GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2. Parse the CSV and post journal entries:
import csv
def _process_settlement_report(self, report_id):
# 1. Request the report
resp = self._signed_request('POST', '/reports/2021-06-30/reports', json={
'reportType': 'GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2',
'marketplaceIds': [self.marketplace_ids],
})
# 2. Poll until DONE; fetch the document URL; download
# 3. Parse rows and create the journal entry
move_lines = []
with open('/tmp/settlement.csv') as f:
for row in csv.DictReader(f, delimiter='\t'):
account = self._map_amazon_fee_to_account(row['transaction-type'])
move_lines.append((0, 0, {
'account_id': account.id,
'debit': max(0, -float(row['amount'])),
'credit': max(0, float(row['amount'])),
'name': row['description'],
}))
self.env['account.move'].create({
'move_type': 'entry',
'journal_id': self.env.ref('account.journal_amazon_settlement').id,
'line_ids': move_lines,
}).action_post()
Verification: the journal entry totals match the bank deposit amount Amazon paid you.
8. Push tracking back for MFN orders
For Merchant Fulfilled Network orders, after the Odoo Delivery is validated with a tracking number, push to Amazon:
def _push_fulfillment(self, picking):
self._signed_request('POST', '/orders/v0/orders/{order_id}/shipment', json={
'shipmentDate': fields.Datetime.now().isoformat(),
'carrierName': picking.carrier_id.name,
'trackingNumber': picking.carrier_tracking_ref,
'shipFromAddress': self._company_address(),
})
Verification: Amazon order page shows "Shipped" status with the tracking number within minutes.
Common mistakes
- Skipping AWS Sigv4 signing. SP-API rejects unsigned requests with a confusing 403.
- Hardcoding the marketplace endpoint. NA, EU, and FE have different base URLs.
- Polling orders more than every 60 seconds. SP-API rate limits aggressively (5 burst, 0.0167/sec sustained for orders).
- Not idempotent on order creation. A network blip causes duplicate orders. Always check
amazon_order_idfirst. - Missing fee mapping. Amazon has 50+ fee types (FBA fee, referral fee, storage fee). Map each to the right Odoo expense account.
Going further
Multi-region sellers: register one app per region (NA + EU + FE), each with its own refresh token. The Odoo backend per region pulls in parallel. North American sellers expanding to UK/EU multiply their addressable market roughly 3x but the listings, taxes, and returns logistics per region need different tuning.
Listing automation: push new Odoo products to Amazon as listings via /listings/2021-08-01/items. Includes images, bullet points, search terms, and category-specific attributes. Each Amazon category has its own attribute schema — pull the schema dynamically and map your Odoo fields.
Buy Box monitoring: pull pricing for your ASINs every hour and alert if you lose the Buy Box. Drives competitive repricing — most ECOSIRE clients run a small ML repricer that adjusts prices within a band to maintain Buy Box ownership while protecting margin.
Returns and refunds: subscribe to the Returns API and create return pickings + credit notes automatically. Returns are a major P&L drag for high-volume sellers; automate the processing to reduce labor cost.
Sponsored Products / Brands ads attribution: pull ad spend from Amazon Advertising API and attribute to the orders. Compute per-keyword ROAS visible in Odoo CRM.
Inventory aging in FBA: FBA storage fees climb with age. Pull the FBA inventory aging report and trigger clearance pricing for SKUs aged 180+ days.
Account health monitoring: pull the Account Health report daily. Flag any negative metrics (late shipments, customer complaints) for immediate intervention.
Vine reviews tracking: for new products in Vine, track review velocity and sentiment. Pair with sales data to see when 5-7 reviews tip the conversion rate.
Brand registry integration: if you own a registered brand on Amazon, pull the Brand Analytics reports for share-of-search, top search terms, and market basket analysis.
Q4 surge planning: build a forecasting model that scales daily inventory targets through October-December based on historical seasonality.
Cross-marketplace inventory pooling: if you sell on multi-marketplaces (Amazon US + Amazon CA + Walmart + your own DTC), build a unified inventory layer that allocates across channels based on velocity and margin.
Settlement-to-bank reconciliation: pair the settlement journal with bank statement parsing so cash receipts auto-clear settlement liability accounts. End-to-end automation saves the AR clerk hours per month.
For full Amazon + Odoo integration including multi-region, FBA reconciliation, and Buy Box automation, ECOSIRE custom Odoo development builds the complete stack. Pair this with how to configure Odoo warehouse routes for the FBA picking flow.
Frequently Asked Questions
What does SP-API cost?
The API itself is free for sellers, but a Professional Seller plan ($39.99/month US) is required. For very high volume there are throttling limits that can be raised by request — Amazon evaluates case by case.
Can I sync historical orders?
Yes, but Amazon's Orders API only goes back 2 years. For older history, pull settlement reports going back further (those are available longer).
How do I handle multiple seller accounts in one Odoo?
One backend record per Amazon account. Tag each SO with amazon_account_id so reporting can split by account.
What about VAT/sales tax?
Amazon withholds VAT in the EU/UK and remits it directly. The settlement report shows VAT-collected lines that should be posted to a "VAT collected by Amazon" liability account, then cleared when Amazon remits.
For the full Amazon + Odoo + accounting integration including settlement reconciliation and tax handling, our Odoo integration team ships fixed-price engagements. Or read how to integrate Shopify with Odoo if Amazon is one of multiple channels.
تحریر
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Odoo ERP کے ساتھ اپنے کاروبار کو تبدیل کریں
آپ کے کاموں کو ہموار کرنے کے لیے ماہر Odoo کا نفاذ، حسب ضرورت، اور معاونت۔
متعلقہ مضامین
How to Add a Custom Button to an Odoo Form View (2026)
Add custom action buttons to Odoo 19 form views: Python action method, view inheritance, conditional visibility, confirmation dialogs. Production-tested.
How to Add a Custom Field in Odoo Without Studio (2026)
Add custom fields via custom module in Odoo 19: model inheritance, view extension, computed fields, store/non-store decisions. Code-first, version-controlled.
How to Add a Custom Report in Odoo Using External Layout
Build a branded PDF report in Odoo 19 using web.external_layout: QWeb template, paperformat, action binding. With print logo + footer overrides.