Skip to content

How to Connect DocuSign to Odoo: E-Signature Integration Guide

DeployMonkey Team · March 23, 2026 10 min read

Why Connect DocuSign to Odoo?

DocuSign is the industry standard for electronic signatures, used by over a million companies worldwide. Integrating it with Odoo transforms your quote-to-close workflow. Instead of emailing PDFs, waiting for printed signatures, and manually updating order status, DocuSign automates the entire process. Sales quotes get signed in minutes, not days.

The typical workflow without integration involves exporting a PDF from Odoo, uploading it to DocuSign, sending it for signature, waiting for completion, then manually confirming the sale order in Odoo. With integration, this becomes a single click.

Integration Architecture

The integration works through DocuSign's eSignature REST API and Odoo's XML-RPC API:

  1. User clicks "Send for Signature" on an Odoo sale order or contract
  2. Odoo generates the PDF and sends it to DocuSign via API
  3. DocuSign sends the document to the recipient for signing
  4. DocuSign fires a webhook when the document is signed
  5. Odoo receives the webhook and updates the record status (confirms the SO, attaches signed PDF)

Prerequisites

  • Odoo 17+ with Sales module
  • DocuSign developer account (free for testing)
  • DocuSign eSignature API access (included in Business Pro plan, $40/user/mo)
  • HTTPS-enabled Odoo instance for webhook callbacks

Step 1: Set Up DocuSign API

  1. Create an account at developers.docusign.com
  2. Go to Apps and Keys
  3. Create a new app — note the Integration Key (client ID)
  4. Add a redirect URI for OAuth: https://your-odoo.com/docusign/callback
  5. Generate an RSA keypair for JWT authentication (server-to-server)

Step 2: Build the Odoo Module

Create a custom module that adds a "Send for Signature" button to sale orders:

class SaleOrder(models.Model):
    _inherit = 'sale.order'
    
    docusign_envelope_id = fields.Char('DocuSign Envelope ID', readonly=True)
    docusign_status = fields.Selection([
        ('sent', 'Sent'),
        ('delivered', 'Delivered'),
        ('signed', 'Signed'),
        ('declined', 'Declined'),
    ], string='Signature Status')
    
    def action_send_docusign(self):
        pdf = self.env['ir.actions.report']._render_qweb_pdf(
            'sale.action_report_saleorder', self.ids
        )
        envelope_id = self._create_docusign_envelope(pdf[0])
        self.write({
            'docusign_envelope_id': envelope_id,
            'docusign_status': 'sent',
        })

Step 3: Create DocuSign Envelope

import requests, base64

def _create_docusign_envelope(self, pdf_content):
    token = self._get_docusign_token()
    
    envelope = {
        'emailSubject': f'Please sign: {self.name}',
        'documents': [{
            'documentBase64': base64.b64encode(pdf_content).decode(),
            'name': f'{self.name}.pdf',
            'documentId': '1',
        }],
        'recipients': {
            'signers': [{
                'email': self.partner_id.email,
                'name': self.partner_id.name,
                'recipientId': '1',
                'tabs': {
                    'signHereTabs': [{
                        'documentId': '1',
                        'pageNumber': '1',
                        'xPosition': '100',
                        'yPosition': '700',
                    }]
                }
            }]
        },
        'status': 'sent',
        'eventNotification': {
            'url': f'{self.env["ir.config_parameter"].sudo().get_param("web.base.url")}/docusign/webhook',
            'envelopeEvents': [
                {'envelopeEventStatusCode': 'completed'},
                {'envelopeEventStatusCode': 'declined'},
            ]
        }
    }
    
    resp = requests.post(
        f'{DOCUSIGN_BASE}/envelopes',
        headers={'Authorization': f'Bearer {token}'},
        json=envelope
    )
    return resp.json()['envelopeId']

Step 4: Handle Completion Webhook

class DocuSignWebhook(http.Controller):
    @http.route('/docusign/webhook', type='json', auth='none', csrf=False)
    def handle(self, **kwargs):
        data = json.loads(http.request.httprequest.data)
        envelope_id = data['data']['envelopeId']
        status = data['event']
        
        order = http.request.env['sale.order'].sudo().search(
            [('docusign_envelope_id', '=', envelope_id)], limit=1
        )
        if order and status == 'envelope-completed':
            order.write({'docusign_status': 'signed'})
            order.action_confirm()  # Auto-confirm the sale order
            # Download and attach signed PDF
            self._attach_signed_document(order, envelope_id)

Odoo's Built-in Sign Module

Odoo Enterprise includes a Sign module that provides basic e-signature functionality. How it compares to DocuSign:

FeatureOdoo SignDocuSign
CostIncluded in Enterprise$40+/user/mo
Legal validityBasic e-signatureAdvanced + qualified signatures
ComplianceeIDAS SESeIDAS AES/QES, ESIGN, UETA
Template libraryBasicExtensive
Audit trailBasicDetailed certificate of completion
Mobile signingWeb-basedNative mobile app

Use Odoo Sign for internal documents and simple agreements. Use DocuSign when you need legally binding signatures with full audit trails, especially for regulated industries or high-value contracts.

DeployMonkey + DocuSign

DeployMonkey instances come with HTTPS and stable URLs, which are required for DocuSign webhook callbacks. Our AI agent can help you configure the integration module and test the signing workflow.