Enable Dark Mode!
how-to-sent-email-verification-code-in-odoo-16.jpg
By: Salman H

How to Sent Email Verification Code in Odoo 16

Technical Odoo 16 Odoo Enterprises Odoo Community

In modern web applications, confirming user input with an email verification code is a common way to ensure that the person filling out your form is the real owner of the email address.

Odoo 16, with its flexible controller and template system, makes this process relatively straightforward — whether you’re working on a contact form, a newsletter subscription, or a registration page.

In this guide, we’ll create a public-facing frontend form that sends an email with a One-Time Password (OTP) to the user and verifies it before completing the form submission.

Why Add Email Confirmation to a Form?

While login security is important, frontend forms also face spam, fake entries, and bots. Adding an email confirmation code step:

  • Prevents fake submissions.
  • Ensures the email address is valid and accessible.
  • Reduces spam without relying solely on CAPTCHA.

Step 1: Module Structure

We’ll create a new module named frontend_email_otp.

__manifest__.py

{
    'name': 'Frontend Form Email OTP',
    'version': '16.0.1.0.0',
    'depends': ['website', 'mail'],
    'data': [
        'data/email_template.xml',
        'views/form_templates.xml',
    ],
    'installable': True,
}

Step 2: Email Template

This template will be used to send the OTP code.

data/email_template.xml

<odoo>
   <record id="frontend_email_otp_template" model="mail.template">
       <field name="name">Frontend Form OTP</field>
       <field name="model_id" ref="base.model_res_partner"/>
       <field name="subject">Your Verification Code</field>
       <field name="body_html" type="html">
                   <p>Hello,</p>
                   <p>Your verification code is: <strong><t t-esc="ctx['data']['code']"/></strong></p>
                   <p>This code will expire in 10 minutes.</p>
           </field>
       <field name="email_to">{{ object.email }}</field>
       <field name="auto_delete" eval="True"/>
   </record>
</odoo>

Step 3: Controller Logic

We’ll handle two steps:

  1. User fills in the form with an email.
  2. System sends an OTP and shows a second form for code verification.

controllers/main.py

from odoo import http
from odoo.http import request
import random, string
from datetime import datetime, timedelta
class FrontendEmailOTP(http.Controller):
    _otp_storage = {}  # Temporary storage (for demo only, use model in production)
    @http.route('/custom/form', type='http', auth='public', website=True)
    def custom_form(self, **kwargs):
        return request.render('frontend_email_otp.custom_form_template')
    @http.route('/custom/form/send_otp', type='http', auth='public', methods=['POST'], website=True, csrf=False)
    def send_otp(self, **post):
        email = post.get('email')
        otp = ''.join(random.choices(string.digits, k=6))
        expiry = datetime.now() + timedelta(minutes=10)
        self._otp_storage[email] = {'code': otp, 'expiry': expiry}
        # Send email
        partner = request.env['res.partner'].sudo().create({'name': email, 'email': email, 'verification_code': otp})
        template = request.env.ref('frontend_email_otp.frontend_email_otp_template')
        if template:
            template.sudo().send_mail(partner.id, force_send=True)
        return request.render('frontend_email_otp.otp_verification_template', {'email': email})
    @http.route('/custom/form/verify_otp', type='http', auth='public', methods=['POST'], website=True, csrf=False)
    def verify_otp(self, **post):
        email = post.get('email')
        otp_input = post.get('otp')
        otp_data = self._otp_storage.get(email)
        if otp_data and otp_data['code'] == otp_input and datetime.now() <= otp_data['expiry']:
            return request.render('frontend_email_otp.success_template', {'email': email})
        else:
            return request.render('frontend_email_otp.otp_verification_template', {
                'email': email,
                'error': 'Invalid or expired code'
            })

Step 4: Templates

views/form_templates.xml

<odoo>
    <!-- Initial Form -->
    <template id="custom_form_template" name="Custom Form">
        <t t-call="website.layout">
            <div class="container mt-5">
                <h2>Custom Form</h2>
                <form action="/custom/form/send_otp" method="post">
                    <div class="form-group mb-3">
                        <label>Email:</label>
                        <input type="email" name="email" class="form-control" required/>
                    </div>
                    <button class="btn btn-primary">Send Verification Code</button>
                </form>
            </div>
        </t>
    </template>
    <!-- OTP Verification -->
    <template id="otp_verification_template" name="OTP Verification">
        <t t-call="website.layout">
            <div class="container mt-5">
                <h2>Enter Verification Code</h2>
                <t t-if="error">
                    <div class="alert alert-danger"><t t-esc="error"/></div>
                </t>
                <form action="/custom/form/verify_otp" method="post">
                    <input type="hidden" name="email" t-att-value="email"/>
                    <div class="form-group mb-3">
                        <input type="text" name="otp" placeholder="Enter code" class="form-control" required/>
                    </div>
                    <button class="btn btn-success">Verify</button>
                </form>
            </div>
        </t>
    </template>
    <!-- Success Page -->
    <template id="success_template" name="Success Page">
        <t t-call="website.layout">
            <div class="container mt-5">
                <h2>Verification Successful!</h2>
                <p>Email <b><t t-esc="email"/></b> has been verified.</p>
            </div>
        </t>
    </template>
</odoo>

How It Works

  • User opens the form/custom/form displays a simple email field.

How to Sent Email Verification Code in Odoo 16-cybrosys

  • Email submitted/custom/form/send_otp:
    • Generates a random 6-digit code.
    • Stores it temporarily (in _otp_storage here, but a model is better in production).
    • Sends the code via Odoo’s mail system.
  • User enters the OTP /custom/form/verify_otp checks:
How to Sent Email Verification Code in Odoo 16-cybrosys
  • If code matches.
  • If still within 10-minute expiry.
  • Success page — if valid, the user is confirmed.
  • How to Sent Email Verification Code in Odoo 16-cybrosys

    Security & Improvements

    For production use:

    • Replace _otp_storage with a proper model for persistence.
    • Add rate limiting to prevent abuse.
    • Implement a Resend OTP button.
    • Store a hash of the OTP instead of the raw code for better security.
    • Use server actions or background jobs for high email volumes.

    Conclusion

    By leveraging Odoo 16’s website module and mail templates, we’ve created a two-step verification process for any public form. This approach is highly flexible — you can adapt it to:

    • Newsletter signups.
    • Event registrations.
    • User onboarding forms.
    • With just a small amount of custom code, you can dramatically improve the integrity of your form data and reduce spam submissions.

      To read more about How to Send Email From Code in Odoo 17, refer to our blog How to Send Email From Code in Odoo 17


    If you need any assistance in odoo, we are online, please chat with us.



    0
    Comments



    Leave a comment



    whatsapp_icon
    location

    Calicut

    Cybrosys Technologies Pvt. Ltd.
    Neospace, Kinfra Techno Park
    Kakkancherry, Calicut
    Kerala, India - 673635

    location

    Kochi

    Cybrosys Technologies Pvt. Ltd.
    1st Floor, Thapasya Building,
    Infopark, Kakkanad,
    Kochi, India - 682030.

    location

    Bangalore

    Cybrosys Techno Solutions
    The Estate, 8th Floor,
    Dickenson Road,
    Bangalore, India - 560042

    Send Us A Message