Skip to content

Odoo Integration Guide

This guide walks you through connecting your Tajir store with Odoo 16+.

Architecture

  • Pull: Odoo calls the Tajir REST API to fetch data
  • Push: Tajir sends webhooks to your Odoo controller

Prerequisites

  • Odoo 16.0+ (Community or Enterprise)
  • A Tajir store on any paid plan
  • An API token from the admin dashboard
  • A publicly accessible Odoo instance (for webhooks)

Step 1: Generate API Credentials

  1. Admin DashboardIntegrations → Create API token
  2. Create webhook endpoint: https://your-odoo.com/tajir/webhook
  3. Save the signing secret

Step 2: Data Mapping

Products

TajirOdoo ModelOdoo Field
productsproduct.templatename, list_price, default_code, barcode
product_variantsproduct.productproduct_tmpl_id, attribute_value_ids
categoriesproduct.categoryname, parent_id

Orders

TajirOdoo ModelOdoo Field
orderssale.orderpartner_id, date_order, state
order_itemssale.order.lineproduct_id, product_uom_qty, price_unit

Status Mapping: pendingdraft, processingsale, completeddone, cancelledcancel

Customers

TajirOdooField
nameres.partnername
phoneres.partnerphone
emailres.partneremail
cityres.partnercity

Step 3: Odoo Webhook Controller

import hmac, hashlib, json
from odoo import http
from odoo.http import request
class TajirWebhookController(http.Controller):
@http.route('/tajir/webhook', type='json', auth='none', methods=['POST'], csrf=False)
def handle_webhook(self):
payload = request.httprequest.get_data()
signature = request.httprequest.headers.get('X-Tajir-Signature', '')
event_type = request.httprequest.headers.get('X-Tajir-Event', '')
secret = request.env['ir.config_parameter'].sudo().get_param('tajir.webhook_secret')
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
return {'error': 'Invalid signature'}
data = json.loads(payload)
if event_type == 'order.created':
self._create_order(data['data'])
elif event_type == 'product.created':
self._create_product(data['data'])
return {'status': 'ok'}
def _create_order(self, order_data):
partner = self._get_or_create_partner(order_data)
so = request.env['sale.order'].sudo().create({
'partner_id': partner.id,
'client_order_ref': order_data['id'][:8].upper(),
})
for item in order_data.get('items', []):
product = request.env['product.product'].sudo().search(
[('default_code', '=', item.get('product_id'))], limit=1)
if product:
request.env['sale.order.line'].sudo().create({
'order_id': so.id,
'product_id': product.id,
'product_uom_qty': item['quantity'],
'price_unit': item['unit_price'],
})
so.action_confirm()
def _create_product(self, data):
request.env['product.template'].sudo().create({
'name': data['name'],
'list_price': data['price'],
'default_code': data.get('sku', ''),
'barcode': data.get('barcode', ''),
})
def _get_or_create_partner(self, data):
Partner = request.env['res.partner'].sudo()
phone = data.get('customer_phone', '')
partner = Partner.search([('phone', '=', phone)], limit=1)
if not partner:
partner = Partner.create({
'name': data.get('customer_name', 'Unknown'),
'phone': phone,
})
return partner

Step 4: Initial Data Sync

import requests, json
API_URL = 'https://api.durj.ly/api/v1'
HEADERS = {'Authorization': 'Bearer tjr_your_token'}
def sync_products():
cursor = None
while True:
params = {'cursor': cursor} if cursor else {}
resp = requests.get(f'{API_URL}/products/export', headers=HEADERS, params=params)
if resp.status_code == 429:
import time; time.sleep(int(resp.headers.get('Retry-After', 60))); continue
for line in resp.text.strip().split('\n'):
if line: create_odoo_product(json.loads(line))
cursor = resp.headers.get('X-Next-Cursor')
if resp.headers.get('X-Has-More') != 'true': break

Troubleshooting

ProblemSolution
Webhooks not arrivingCheck endpoint is publicly accessible and returns 200
Signature verification failsVerify raw body bytes, not parsed JSON
Rate limited during syncAdd delays, respect Retry-After header
Duplicate ordersUse client_order_ref to check before creating