Paying $12K for Zapier to Sync Files? Connect Odoo to SharePoint with Graph API
By Braincuber Team
Published on December 20, 2025
Your sales team closes deals in Odoo. Your legal team stores contracts in SharePoint. Every closed deal requires: Export PDF from Odoo → Download to computer → Upload to SharePoint → Manually rename file → Move to correct folder. Your operations manager does this 47 times per month. Each upload takes 3.2 minutes.
Time wasted: 2.5 hours monthly just moving files between systems. Cost: $70/month in manual labor. Plus the errors: 8 contracts uploaded to wrong folders last quarter. Your legal team couldn't find them. One customer contract was lost for 11 days. You almost breached SLA.
Worse: Your IT department says "SharePoint and Odoo can't talk to each other." They want to buy a $12K/year middleware tool (Zapier Enterprise, MuleSoft). Your CFO asks: "Can't we just connect them directly?"
Yes. Microsoft Graph API lets Odoo upload files directly to SharePoint. No middleware. No manual copying. When sales order confirms, contract auto-uploads to SharePoint folder. Here's how to build it without spending $12K on integration tools.
You're Wasting Money If:
What Microsoft Graph API Actually Does
Single REST API endpoint that accesses all Microsoft 365 services. Upload files to SharePoint, read emails from Outlook, get calendar events—all from one API. For Odoo, we use it to automate file uploads to SharePoint.
| Method | Setup Time | Annual Cost |
|---|---|---|
| Manual copying | 0 (already doing it) | $840 in labor |
| Zapier Enterprise | 2 hours | $12,000 subscription |
| Graph API (Direct) | 4 hours (one-time) | $0 |
Prerequisites
Before starting, you need:
- Microsoft 365 account with SharePoint access
- Azure AD admin permissions (to create app registrations)
- Odoo instance with developer mode enabled
- Python knowledge (you'll write custom Odoo code)
Step 1: Create Azure App Registration
You're creating an "app" in Azure that represents your Odoo instance. This app gets credentials to access SharePoint.
Register the App
- Go to portal.azure.com
- Navigate to Azure Active Directory
- Click App registrations (left menu)
- Click New registration
- Enter name:
Odoo SharePoint Integration - Supported account types: Accounts in this organizational directory only
- Redirect URI: Leave blank (not needed for server-to-server)
- Click Register
Copy Credentials
After registration, you'll see the app overview page. Copy these values:
Application (client) ID: 12345678-1234-1234-1234-123456789abc
Directory (tenant) ID: 87654321-4321-4321-4321-cba987654321
Save these somewhere. You'll need them in Odoo.
Step 2: Configure API Permissions
Tell Azure what your app is allowed to do in SharePoint.
- In your app, click API permissions (left menu)
- Click Add a permission
- Select Microsoft Graph
- Select Application permissions (not Delegated)
- Search for:
Sites.ReadWrite.All - Check the box next to it
- Click Add permissions
- Click Grant admin consent for [Your Organization]
- Confirm
Why "Application permissions"? This is for server-to-server auth (Odoo → SharePoint). "Delegated permissions" are for user login flows, which we don't need here.
Step 3: Create Client Secret
This is like a password for your app. Odoo uses it to prove it's authorized.
- In your app, click Certificates & secrets
- Click New client secret
- Description:
Odoo Integration Key - Expires: 24 months (set calendar reminder to rotate)
- Click Add
- IMMEDIATELY COPY THE VALUE (you can't see it again)
Client Secret Value: abc123DEF456ghi789JKL012mno345PQR678
Warning: If you close the page without copying, the secret is lost forever. You'll have to create a new one.
Step 4: Get SharePoint Details
You need to know where to upload files in SharePoint.
Find Your Tenant Name
Your SharePoint URL looks like: https://yourcompany.sharepoint.com
The tenant name is: yourcompany
Find Your Site Name
If your site URL is: https://yourcompany.sharepoint.com/sites/Sales
The site name is: Sales
Find Your Drive Name
This is your document library name, usually Documents or Shared Documents.
Step 5: Store Credentials in Odoo
Save all these values in Odoo's System Parameters (secure config storage).
- Go to Settings → Technical → Parameters → System Parameters
- Click Create for each parameter:
Key: sharepoint.client_id
Value: 12345678-1234-1234-1234-123456789abc
Key: sharepoint.client_secret
Value: abc123DEF456ghi789JKL012mno345PQR678
Key: sharepoint.tenant_id
Value: 87654321-4321-4321-4321-cba987654321
Key: sharepoint.tenant_name
Value: yourcompany
Key: sharepoint.site_name
Value: Sales
Key: sharepoint.drive_name
Value: Documents
Step 6: Write Python Code to Get Access Token
Now the fun part: Make Odoo talk to Microsoft. Create a Python method in your custom Odoo module.
Get System Parameters
from odoo import models, api
import requests
class SharePointConnector(models.Model):
_name = 'sharepoint.connector'
_description = 'SharePoint Integration'
def _get_config_param(self, key):
"""Helper to fetch system parameters"""
return self.env['ir.config_parameter'].sudo().get_param(key)
def get_access_token(self):
"""Authenticate with Microsoft and get access token"""
client_id = self._get_config_param('sharepoint.client_id')
client_secret = self._get_config_param('sharepoint.client_secret')
tenant_id = self._get_config_param('sharepoint.tenant_id')
token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
payload = {
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
'scope': 'https://graph.microsoft.com/.default'
}
response = requests.post(token_url, data=payload)
if response.status_code != 200:
raise Exception(f"Failed to get token: {response.text}")
return response.json()['access_token']
Step 7: Get SharePoint Site ID
Before uploading files, you need the Site ID (unique identifier for your SharePoint site).
def get_site_id(self):
"""Get SharePoint site ID"""
token = self.get_access_token()
tenant_name = self._get_config_param('sharepoint.tenant_name')
site_name = self._get_config_param('sharepoint.site_name')
url = f"https://graph.microsoft.com/v1.0/sites/{tenant_name}.sharepoint.com:/sites/{site_name}"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json'
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to get site: {response.text}")
return response.json()['id']
Step 8: Get Drive (Document Library) ID
def get_drive_id(self, site_id):
"""Get document library (drive) ID"""
token = self.get_access_token()
drive_name = self._get_config_param('sharepoint.drive_name')
url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json'
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to get drives: {response.text}")
# Find drive by name
drives = response.json()['value']
for drive in drives:
if drive['name'] == drive_name:
return drive['id']
raise Exception(f"Drive '{drive_name}' not found")
Step 9: Upload File to SharePoint
The main event: Upload a file from Odoo to SharePoint.
def upload_file(self, file_content, file_name, folder_path=''):
"""
Upload file to SharePoint
:param file_content: Binary file content
:param file_name: Name of file (e.g., 'contract.pdf')
:param folder_path: Path in SharePoint (e.g., 'Contracts/2025')
"""
token = self.get_access_token()
site_id = self.get_site_id()
drive_id = self.get_drive_id(site_id)
# Construct upload URL
if folder_path:
url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root:/{folder_path}/{file_name}:/content"
else:
url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root:/{file_name}:/content"
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/octet-stream'
}
response = requests.put(url, headers=headers, data=file_content)
if response.status_code not in [200, 201]:
raise Exception(f"Upload failed: {response.text}")
return response.json()
Step 10: Trigger Upload from Sales Order
Now integrate this into your workflow. Example: When sales order confirms, upload contract PDF to SharePoint.
from odoo import models
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
"""Override confirm to upload contract"""
res = super().action_confirm()
# Generate PDF report
pdf_content = self.env.ref('sale.action_report_saleorder')._render_qweb_pdf([self.id])[0]
# Upload to SharePoint
connector = self.env['sharepoint.connector'].create({})
file_name = f"SO{self.name.replace('/', '_')}.pdf"
folder_path = f"Sales/Contracts/{self.date_order.year}"
try:
result = connector.upload_file(
file_content=pdf_content,
file_name=file_name,
folder_path=folder_path
)
self.message_post(
body=f"Contract uploaded to SharePoint: {result['webUrl']}"
)
except Exception as e:
self.message_post(
body=f"SharePoint upload failed: {str(e)}"
)
return res
Real-World Example: Full Workflow
Scenario: Auto-Upload Sales Contracts
- Sales rep creates quotation in Odoo (SO0047)
- Customer approves and signs electronically
- Sales rep clicks "Confirm" in Odoo
- Odoo automatically:
- Generates contract PDF
- Gets access token from Microsoft
- Uploads to SharePoint:
Sales/Contracts/2025/SO0047.pdf - Posts SharePoint link in Odoo chatter
- Legal team opens SharePoint, sees contract already filed
- Time saved: 3.2 minutes per order × 47 orders/month = 2.5 hours
Result: Zero manual uploads
Operations manager stops copy-pasting files. Legal team always finds documents.
Advanced: List Files in SharePoint Folder
You can also read files from SharePoint into Odoo.
def list_files(self, folder_path=''):
"""List files in SharePoint folder"""
token = self.get_access_token()
site_id = self.get_site_id()
drive_id = self.get_drive_id(site_id)
if folder_path:
url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root:/{folder_path}:/children"
else:
url = f"https://graph.microsoft.com/v1.0/sites/{site_id}/drives/{drive_id}/root/children"
headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/json'
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to list files: {response.text}")
return response.json()['value']
Common Mistakes That Break Integration
1. Using Delegated Permissions Instead of Application
Token request fails with "invalid_grant" error. You selected "Delegated" when you needed "Application."
Fix: Delete permission, add Sites.ReadWrite.All as Application permission.
2. Forgetting Admin Consent
Permission added but not granted. Token works but API calls return "Insufficient privileges."
Fix: Click "Grant admin consent" button in Azure app permissions.
3. Wrong Tenant/Site Names
Site ID request fails. You typed mycompany-admin when tenant is mycompany.
Fix: Check SharePoint URL. Tenant name is first part before .sharepoint.com.
4. Client Secret Expired
Integration worked for 24 months, suddenly stopped. Secret expired.
Fix: Create new client secret in Azure, update Odoo system parameter. Set calendar reminder 2 weeks before expiry.
ROI: Direct Integration vs Middleware
Annual Cost Comparison:
Manual Process:
- Time: 3.2 min/upload × 47 uploads/month = 2.5 hours/month
- Cost: 30 hours/year @ $28/hour = $840/year
- Errors: 8 misplaced files/quarter × 45 min recovery = 6 hours/year = $168/year
- Total: $1,008/year
Zapier Enterprise:
- Subscription: $12,000/year
- Setup time: 2 hours @ $85/hour = $170 (one-time)
- Errors: Minimal
- Total: $12,170 (year 1)
Graph API (Direct):
- Development: 4 hours @ $85/hour = $340 (one-time)
- Maintenance: 30 min/year = $42/year
- Cost: $0 (API is free)
- Total: $382 (year 1), $42/year after
Savings vs Zapier: $11,788/year
Plus you own the code. No vendor lock-in. No subscription increases.
Quick Implementation Checklist
- Create Azure app registration (10 min)
- Add Sites.ReadWrite.All permission (Application type)
- Grant admin consent for permission
- Create client secret (copy immediately)
- Store credentials in Odoo system parameters
- Write Python methods (token, site ID, drive ID, upload)
- Test upload with sample file
- Integrate into workflow (Sales, HR, Projects)
- Set calendar reminder for secret rotation in 23 months
Security Tip: Never hardcode client secrets in code. Always use system parameters. Add IP whitelist in Azure if possible. Rotate secrets annually even if they're valid for 24 months.
Paying $12K/Year for Zapier to Sync Files?
We build direct Odoo-SharePoint integrations using Microsoft Graph API. Auto-upload contracts, invoices, reports. No middleware subscriptions. Own the code forever. Save $11K+ annually.
