If your business operates in Latin America — or sells into LATAM from the United States — Mercado Libre is not a channel you can manage manually. With over 100 million active buyers across Mexico, Colombia, Argentina, Brazil, and Chile, it's the backbone of B2C ecommerce in the region. When your ERP is Odoo, the integration between these two systems has to be airtight: same product, same stock, same order — every time, in every country, without manual reconciliation.
The challenge is that MELI and Odoo think about commerce very differently. MELI thinks in items, variations, catalog listings, FULL fulfillment, and Mercado Pago. Odoo thinks in product templates, sale orders, stock pickings, and accounting journals. Bridging them correctly — for Mexico's OXXO Pay, Colombia's PSE, Argentina's catalog mode, Brazil's CNPJ requirements, and US cross-border sellers operating MLM simultaneously — is what this post is about.
MELI is not one market — it's six
Every Mercado Libre site is a separate marketplace with its own Site ID, currency, payment methods, carrier ecosystem, tax requirements, and catalog behavior. Your integration architecture must treat them as six separate integrations sharing a common API — not as one integration with minor localization tweaks.
What changes per country in your Odoo integration
| Component | Mexico (MLM) | Colombia (MCO) | Argentina (MLA) | Brazil (MLB) |
|---|---|---|---|---|
| Odoo operating unit | MX subsidiary | CO subsidiary | AR subsidiary | BR subsidiary |
| Currency on sale.order | MXN | COP | ARS | BRL |
| Cash payment journals | OXXO Pay, SPEI | PSE, Efecty, Baloto | Rapipago, Pago Fácil | Boleto, Pix |
| Tax ID field on partner | RFC (vat field) | NIT (vat field) | CUIT (vat field) | CNPJ/CPF (vat field) |
| Catalog mode risk | Low | Low | High — very common | High — very common |
| MELI FULL availability | ✓ Available (CDMX, MTY) | ✓ Available (Bogotá) | ✓ Available (GBA) | ✓ Available (SP, RJ) |
| Installment handling | Meses sin intereses | Cuotas | Cuotas (critical) | Parcelamento (critical) |
The five data flows you must build
Every MELI ↔ Odoo integration is actually five separate integrations sharing the same auth layer. Treating them as one breaks when you scale — because each flow has different directionality, different triggers, different failure modes, and different LATAM-specific requirements.
Catalog — Odoo → MELI (with country exceptions)
Product titles, descriptions, prices, images, attributes, and variations push from Odoo to MELI. Exception for MLA/MLB: In Argentina and Brazil, catalog mode assigns a canonical title/description managed by MELI itself. Never push content fields for catalog-mode items — only price and stock. Your Data Mapper must check item.catalog_listing = true before any content write.
Inventory — Bidirectional, Odoo as master (except FULL)
For standard fulfillment, Odoo is master — stock changes in Odoo push to MELI every 15 minutes plus event-driven on every order. For MELI FULL (Fulfillment by MELI), the direction inverts: MELI knows what's in their fulfillment centers in México City, Bogotá, Buenos Aires, São Paulo — and Odoo must track those as read-only MELI FULL locations, not as your own warehouse stock.
Orders — MELI → Odoo (with LATAM payment complexity)
Every MELI sale must become a confirmed sale.order in Odoo within 2 minutes. The payment method determines downstream behavior: credit cards and Mercado Pago balance confirm immediately and trigger fulfillment. Cash methods — OXXO Pay (MX), PSE bank transfer (CO), Efecty/Baloto (CO), Boleto (BR), Pix (BR) — have clearing delays of 15 minutes to 3 business days. Do not trigger fulfillment until MELI confirms payment received for cash methods.
Fulfillment — Odoo → MELI (reputation-critical)
When your warehouse ships an order and Odoo validates the stock.picking, you must push the tracking number back to MELI within the SLA window. Late confirmation counts against your MercadoLíder score across all your LATAM sites simultaneously — a Mexican seller with Colombian and Argentine stores sees their reputation drop across all three if late shipment rates exceed 4%. The trigger must be event-driven on picking.state = done, not a batch job.
Returns & Claims — MELI → Odoo (the skipped layer)
Returns in LATAM are operationally complex. In Mexico, buyers can initiate returns within 30 days by law. In Colombia, Ley 1480 (Estatuto del Consumidor) governs buyer protection. In Brazil, the Código de Defesa do Consumidor gives buyers up to 7 days for distance purchases. Each country has different regulatory requirements — your Odoo reverse picking and credit note workflow must be country-aware. MELI mediates unresolved disputes via claims, which must map to Odoo CRM for your ops team to manage.
Building the integration — LATAM first
Register one MELI app, authorize one seller account per country
One app at developers.mercadolibre.com, but one OAuth authorization per seller account. A Mexico seller account (MLM) and a Colombia seller account (MCO) are separate entities with separate refresh tokens — even if they belong to the same company. Authorize each with scopes: read, write, offline_access.
Store refresh tokens keyed by seller_id + site_id. MELI rotates refresh tokens on every exchange — persist the new token immediately to the database before proceeding. Losing a refresh token means the seller must manually re-authorize through MELI's web UI.
- MX: Register at mercadolibre.com.mx — seller uses their Mexican RFC-linked account
- CO: mercadolibre.com.co — NIT-linked seller account for business sellers
- AR: mercadolibre.com.ar — CUIT required for MercadoLíder business accounts
- BR: mercadolivre.com.br — separate domain, CNPJ for business accounts
- US cross-border: Enroll in CBT program on mercadolibre.com.mx or mercadolivre.com.br — US company, LATAM-site listing
Add custom fields to Odoo and create one operating unit per country
For multi-country operations, each Mercado Libre site must map to a separate Odoo company or operating unit, with its own currency, fiscal positions, and warehouse. Add these custom fields via Odoo Studio or a module:
x_meli_item_idonproduct.template— MELI item ID per sitex_meli_variation_idonproduct.product— MELI variation IDx_meli_site_idonproduct.template— MLM, MCO, MLA, MLB, MLCx_meli_is_full(boolean) onproduct.product— flags FULL-enrolled SKUsx_meli_catalog_listing(boolean) onproduct.template— prevents content overwrites for MLA/MLB
def upsert_product(item: dict, odoo_company_id: int) -> int: models, uid = odoo_connect() meli_id = item["id"] site_id = item.get("site_id", "") # MLM, MCO, MLA, MLB, MLC is_catalog = item.get("catalog_listing", False) # Lookup by MELI item ID — not title, not SKU existing = models.execute_kw(ODOO_DB, uid, ODOO_KEY, "product.template", "search_read", [[("x_meli_item_id", "=", meli_id), ("x_meli_site_id", "=", site_id)]], {"fields": ["id", "x_meli_catalog_listing"], "limit": 1} ) # Base vals — always safe to update vals = { "list_price": float(item.get("price", 0)), "x_meli_item_id": meli_id, "x_meli_site_id": site_id, "x_meli_catalog_listing": is_catalog, } # Only update content fields if NOT catalog mode (MLA/MLB protection) if not is_catalog: seller_sku = next( (a["value_name"] for a in item.get("attributes", []) if a["id"] == "SELLER_SKU"), meli_id ) vals.update({ "name": item["title"], "default_code": seller_sku, "description": item.get("plain_text", ""), }) if existing: tmpl_id = existing[0]["id"] models.execute_kw(ODOO_DB, uid, ODOO_KEY, "product.template", "write", [[tmpl_id], vals]) else: vals["type"] = "product" vals["sale_ok"] = True tmpl_id = models.execute_kw(ODOO_DB, uid, ODOO_KEY, "product.template", "create", [vals]) return tmpl_id
Build the order ingestion pipeline with LATAM payment routing
Every MELI order becomes an Odoo sale.order. The most LATAM-specific part is payment method handling. Credit card and Mercado Pago wallet payments clear immediately and can trigger fulfillment. Cash-equivalent methods (OXXO, PSE, Efecty, Boleto, Pix) have clearing windows of 15 minutes to 3 business days — orders must be created in a pending payment state and only move to confirmed when MELI sends the payment confirmation webhook.
# MELI payment_type → (Odoo journal name, clears_immediately) # Expand per your Odoo account.journal configuration LATAM_PAYMENT_MAP = { # ── Universal (all LATAM sites) ───────────────────────────────────── "account_money": ("Mercado Pago", True), "visa": ("Mercado Pago", True), "master": ("Mercado Pago", True), "amex": ("Mercado Pago", True), # ── Mexico (MLM) ──────────────────────────────────────────────────── "oxxo": ("OXXO Pay", False), # up to 24h clearing "kueski": ("Kueski Pay", True), "spei": ("Transferencia SPEI", False), # 15min–4h "banamex_3months": ("Mercado Pago", True), # MSI — cleared by MP # ── Colombia (MCO) ────────────────────────────────────────────────── "pse": ("PSE Colombia", False), # 5min–24h "efecty": ("Efecty", False), # next business day "baloto": ("Baloto", False), # next business day "bancolombia": ("Bancolombia", False), # ── Argentina (MLA) ───────────────────────────────────────────────── "rapipago": ("Rapipago", False), "pagofacil": ("Pago Fácil", False), # ── Brazil (MLB) ──────────────────────────────────────────────────── "bolbradesco": ("Boleto Bancário", False), # 1–3 business days "pix": ("Pix", True), # instant "account_money_br": ("Mercado Pago", True), # ── Chile (MLC) ───────────────────────────────────────────────────── "webpay": ("WebPay", True), "khipu": ("Khipu", False), } def get_order_state(payment_type: str) -> str: """Returns Odoo sale.order state based on payment clearing speed.""" _, clears_now = LATAM_PAYMENT_MAP.get(payment_type, ("Mercado Pago", True)) return "sale" if clears_now else "draft" # draft = hold fulfillment
Implement bidirectional inventory with FULL flag awareness
Mercado Libre FULL is now available in Mexico City and Monterrey (MLM), Bogotá (MCO), Greater Buenos Aires (MLA), São Paulo and Rio de Janeiro (MLB). For FULL-enrolled SKUs, MELI controls the inventory at their fulfillment centers — your Odoo should read MELI's reported stock into a dedicated MELI FULL location, not treat it as your own warehouse stock.
SAFETY_BUFFER = 5 # units held back — absorbs sync lag and multi-channel MELI_FULL_LOCATION_ID = int(os.environ["ODOO_MELI_FULL_LOC_ID"]) def sync_inventory(meli_item_id: str, site_id: str, odoo_wh_id: int): models, uid = odoo_connect() products = models.execute_kw(ODOO_DB, uid, ODOO_KEY, "product.product", "search_read", [[("product_tmpl_id.x_meli_item_id", "=", meli_item_id), ("product_tmpl_id.x_meli_site_id", "=", site_id)]], {"fields": ["id", "x_meli_is_full", "x_meli_variation_id"]} ) for prod in products: product_id = prod["id"] is_full = prod.get("x_meli_is_full", False) variation_id = prod.get("x_meli_variation_id") if is_full: # MELI FULL: MELI is master → read stock from MELI → write Odoo FULL location item = meli_get(f"/items/{meli_item_id}") fulfil = item.get("available_quantity", 0) set_odoo_quant(models, uid, product_id, MELI_FULL_LOCATION_ID, fulfil) else: # Standard: Odoo is master → read Odoo → push to MELI quants = models.execute_kw(ODOO_DB, uid, ODOO_KEY, "stock.quant", "search_read", [[("product_id", "=", product_id), ("location_id.usage", "=", "internal"), ("location_id.warehouse_id", "=", odoo_wh_id)]], {"fields": ["quantity", "reserved_quantity"]} ) available = sum(q["quantity"] - q["reserved_quantity"] for q in quants) push_qty = max(0, int(available) - SAFETY_BUFFER) body = ({"variations": [{"id": variation_id, "available_quantity": push_qty}]} if variation_id else {"available_quantity": push_qty}) meli_put(f"/items/{meli_item_id}", body)
Wire fulfillment confirmation to protect your MercadoLíder score
Your MercadoLíder status determines your listing position in search results across every LATAM country you operate in. The late shipment threshold is 4% — across all orders, all countries, all sites simultaneously. A seller with MercadoLíder Gold in Mexico can lose it because of late shipments on Colombian orders. The trigger must be event-driven on stock.picking done, never from a batch job.
- Hook into
stock.pickingwrite whenstate → done(Odoo ORM override or automation rule) - Extract
carrier_tracking_refand map carrier name to MELI carrier codes per country - For MX: Estafeta →
estafeta, DHL →dhl, Redpack →redpack, FedEx →fedex - For CO: Servientrega →
servientrega, Coordinadora →coordinadora, Deprisa →deprisa - For AR: OCA →
oca, Andreani →andreani, Correo Argentino →correoargentino - For BR: Correios →
correios, Jadlog →jadlog, Total Express →total_express
Build the returns layer — legally required in every LATAM country
Consumer protection laws across LATAM mandate return rights that are stricter than most US equivalents. Your integration must handle the full lifecycle:
| Country | Return Window | Governing Law | Odoo Action |
|---|---|---|---|
| 🇲🇽 Mexico | 30 days (distance sales) | LFPC Art. 92 TER | Reverse picking + credit note in MXN |
| 🇨🇴 Colombia | 5 business days | Ley 1480 — Estatuto del Consumidor | Reverse picking + credit note in COP |
| 🇦🇷 Argentina | 10 business days | Ley 24.240 — Defensa del Consumidor | Reverse picking + credit note in ARS |
| 🇧🇷 Brazil | 7 calendar days | Código de Defesa do Consumidor | Reverse picking + credit note in BRL + Nota Fiscal |
| 🇨🇱 Chile | 10 business days | Ley 19.496 — Protección Consumidores | Reverse picking + credit note in CLP |
Monitor reputation metrics per site and alert before thresholds
Pull seller metrics weekly from MELI's API per site. Display them in a Slack digest or Odoo dashboard. Alert your ops team at 50% of threshold — by the time MELI penalizes you, the damage is already done and the recovery window is 60+ days.
REPUTATION_THRESHOLDS = {
# metric_key: (alert_at, penalized_at) — fractions, not percentages
"late_shipment_rate": (0.02, 0.04), # alert at 2%, suspended at 4%
"cancellation_rate": (0.008, 0.015),
"claims_rate": (0.015, 0.030),
}
SITES = ["MLM", "MCO", "MLA", "MLB", "MLC"]
def check_reputation_all_sites(seller_ids: dict) -> list[dict]:
"""
seller_ids: {"MLM": "seller_id_mx", "MCO": "seller_id_co", ...}
Returns list of alerts for any metric near threshold.
"""
alerts = []
for site, seller_id in seller_ids.items():
metrics = meli_get(f"/users/{seller_id}/seller_reputation",
token_manager=get_token_manager(site))
transactions = metrics.get("transactions", {})
tier = metrics.get("level_id", "unknown") # 1_red → 5_green
for metric_key, (alert_at, penalty_at) in REPUTATION_THRESHOLDS.items():
value = transactions.get(metric_key, 0)
if value >= alert_at:
alerts.append({
"site": site,
"metric": metric_key,
"value": round(value * 100, 2),
"limit": round(penalty_at * 100, 2),
"tier": tier,
"critical": value >= penalty_at * 0.85,
})
return sorted(alerts, key=lambda x: x["value"], reverse=True)LATAM-specific mistakes that cost real money
These are not generic integration mistakes. They're the ones that show up specifically in LATAM operations — the payment method edge cases, country-specific legal requirements, and MELI regional behaviors that most integration guides written outside the region simply don't know about.
Fulfilling OXXO / Boleto orders before payment confirms
In Mexico, OXXO Pay takes up to 24 hours to clear. In Brazil, Boleto takes 1–3 business days. Triggering fulfillment the moment the MELI order appears — before payment_status = approved — ships product you may never be paid for. This is the #1 cash flow problem for Mexico and Brazil sellers.
Treating ARS as a stable currency in Odoo
Argentine peso (ARS) can lose 30–40% of value in a year. If your Odoo company currency is USD and you're recording MLA sales in ARS at today's exchange rate, your financial reports will be wrong by month-end, and your Odoo AR balance will look healthy when it's actually worth far less in USD terms.
Overwriting MLA/MLB catalog titles from Odoo
Argentina and Brazil have the highest rate of catalog listings on MELI. Pushing your Odoo product title back to a catalog listing creates an infinite update loop — MELI reverts your title, your sync fires again, MELI reverts again. Your product.template gets updated hundreds of times per day.
Missing RFC / NIT / CNPJ on B2B customers in Odoo
Business buyers on MELI in Mexico (RFC), Colombia (NIT), and Brazil (CNPJ) need their tax ID on the Odoo partner record for CFDI/DIAN/Nota Fiscal invoicing. Skipping this means your accounting team generates thousands of invoices without mandatory tax fields — a legal compliance failure in all three countries.
Single Odoo company for multi-country MELI operations
Running Mexico, Colombia, and Argentina sales all inside one Odoo company means shared chart of accounts, shared currency, shared tax rates. Every financial report is wrong. Every tax filing is impossible. Accountants in all three countries will refuse to certify the accounts.
Ignoring the returns legal deadline by country
Consumer protection law return windows differ significantly: 5 business days in Colombia, 7 calendar days in Brazil, 10 business days in Argentina and Chile, 30 days in Mexico. A single returns SLA will violate at least one country's requirement.
Losing the refresh token because it wasn't persisted
MELI rotates the refresh token on every OAuth exchange. If your service restarts, crashes, or handles an exception between token exchange and persistence, the old token is invalidated and the new one is lost. Every country's seller account must be re-authorized manually.
Not monitoring reputation per site
Late shipments on MLB (Brazil) lower your score on MLM (Mexico) too — MELI reputation is seller-wide, not per-site. US brands selling into LATAM via cross-border are especially at risk: a late shipment caused by US customs delay still counts against your LATAM reputation score.
LATAM readiness checklist
Before going live with any MELI ↔ Odoo integration — whether you're building it or evaluating a vendor — every item below must have a named owner and a documented implementation. "We'll handle that later" on any of these will cost more than the initial build.
🌎 GEO & Multi-Country Setup
- One Odoo company (or operating unit) per country with legal entity — MX, CO, AR, BR, CL
- Currency configured per operating unit: MXN, COP, ARS, BRL, CLP
- Fiscal positions set up per country: Mexican CFDI, Colombian DIAN, Brazilian Nota Fiscal, Argentine AFIP
- Tax ID field (vat) mapped per country: RFC (MX), NIT (CO), CUIT (AR), CNPJ/CPF (BR), RUT (CL)
- Carrier code mapping built per country: MX, CO, AR, BR carriers all mapped to MELI carrier codes
- US cross-border: CBT program enrolled, HS codes on all products, customs delay buffer in promised shipping window
🔐 Auth & Token Management
- One OAuth authorization per seller account per country (not one per app)
- Refresh tokens stored in DB keyed by seller_id + site_id — never in .env or memory
- Token persistence is synchronous and transactional — write before use
- Alert on token exchange failure — immediate Slack/PagerDuty, not next-day log review
- Notification subscriptions registered per seller: orders_v2, items, shipments, claims, stock_locations
- Webhook x-signature validation implemented — tested with tampered payload (should return 401)
- Webhook dedup via Redis SET — tested with duplicate notification replay
💳 Payments & Orders
- All LATAM payment types mapped to Odoo journals per country
- Cash-clearing methods (OXXO, PSE, Efecty, Boleto, Rapipago) hold order in draft until payment_status = approved
- Installment orders (meses sin intereses MX, cuotas AR, parcelamento BR) recorded as single payment — not installments in Odoo
- Order idempotency: client_order_ref = "MELI-{order_id}" checked before create
- 2-minute order polling job as backup for missed webhooks (with idempotency)
- Buyer tax ID (RFC/NIT/CNPJ) extracted and stored on res.partner
📦 Catalog & Inventory
- x_meli_catalog_listing field blocks content pushes for MLA/MLB catalog items
- MELI FULL location created in Odoo — separate from warehouse stock for each FULL-enabled country
- x_meli_is_full flag inverts sync direction for FULL-enrolled variants
- Safety buffer (min 5 units) applied on every stock push — accounts for multi-channel sales lag
- 15-minute scheduled inventory push for all non-FULL active SKUs per site
- Daily catalog reconciliation: MELI active item count vs Odoo x_meli_item_id count per site
🚚 Fulfillment, Returns & Reputation
- Fulfillment confirmation triggered by stock.picking → done event — not by batch job
- Returns layer live at launch — not "phase 2": return request → reverse picking → credit note
- Return window SLAs set per country in Odoo: 30d MX, 5d CO, 10d AR, 7d BR, 10d CL
- Brazil returns: Nota Fiscal de devolução workflow confirmed with accounting team
- Claims routed to Odoo CRM with original order reference and claim ID
- Weekly reputation pull for all sites — late shipment, cancellation, claims metrics
- Alert thresholds: late shipment >2%, cancellation >0.8%, claims >1.5% → immediate ops alert
- MercadoLíder tier visible on ops dashboard per country
Built for your region — not adapted for it
The difference between a MELI ↔ Odoo integration that works in a demo and one that runs at 1,000 orders per day across Mexico, Colombia, Argentina, and Brazil is everything in this post. The OXXO Pay hold logic. The ARS currency volatility handling. The MLA catalog mode protection. The Nota Fiscal de devolução workflow. The carrier code mapping for Coordinadora and Andreani. None of this appears in a generic integration guide written for a US or European audience.
At Molten Logistics, we've built this integration — in this region, for this region. We've run it through MELI API version updates, Mercado Pago payment method changes, FULL expansion across new cities, and the compliance changes that came with Mexico's CFDI 4.0 migration. For US brands expanding into LATAM through Mercado Libre's Cross-Border Trade program, we know exactly what customs documentation your Odoo needs to generate and what shipping windows actually work from US warehouses to Mexican and Colombian buyers.
Built for LATAM — not adapted for it
Luis Alba and the Molten team have deep roots in LATAM ecommerce operations. We've integrated Mercado Libre with Odoo for sellers in Mexico City, Bogotá, Buenos Aires, and São Paulo — and for US brands selling into all four markets simultaneously. We don't adapt generic integrations for your region. We build for it from the start.
See if you qualify →