Advertisement
🌐 Portal Development

Odoo Portal Development – Customer Portal Overview

The Odoo customer portal is an authenticated web area under /my/ where customers can view their own records — orders, invoices, support tickets, and anything else you expose. It sits between the backend (staff-only) and the public website, giving customers self-service access to their data without requiring a backend user licence.

⏱️ 20 min 🎯 Intermediate 📅 Updated 2026

The Odoo Portal: A Secure Window for External Users

The portal is the set of /my/... pages where external users (customers, vendors) log in to see their own documents — orders, invoices, tickets — without access to the internal backend.

ConceptRole
Portal userexternal, limited-access account
/my/* routesthe portal pages (via controllers)
Record ruleslimit each user to their own records
CustomerPortal controllerbase class you extend

The purpose: businesses need to expose some data to customers (your invoices, your order status) without giving them a login to the internal ERP. The portal is that controlled, self-service window — built on web controllers rendering QWeb templates. Portal users are a distinct, restricted group: they can't reach backend menus; they only see the portal pages you build, and only the records that belong to them. Security is the entire point and the entire risk: the mechanism that keeps customer A from seeing customer B's invoices is record rules (row-level security tied to the user's partner) plus careful controller code. Never trust an id from the URL — always verify the logged-in user owns the record before rendering it, or one customer can read another's data by editing the URL number. Extending it: you inherit Odoo's CustomerPortal controller to add your model's pages and reuse the standard portal layout, breadcrumbs, and document counters. Understand the portal as "authenticated, record-scoped, self-service pages for outsiders" and its design makes sense.

🏋️ Practical Exercise

  1. Note that the portal gives customers limited access.
  2. Log in as a portal user.
  3. View a document in the portal.
  4. Note the difference between portal and internal users.
  5. Find the /my routes.

🔥 Challenge Exercise

Explain the Odoo Portal: how external users (customers/vendors) get limited, secure access to their own documents (invoices, orders, tickets) via /my/* routes. Contrast portal users with internal users and public visitors.

What you'll learn:
  • What the Odoo portal is and how it differs from the backend and website
  • How portal.mixin adds portal support to a model
  • Access tokens and _compute_access_url()
  • How to enable portal access for a custom model

What is the Odoo Customer Portal?

The customer portal is a set of web pages at /my/ that authenticated customers (portal users) can access. Out of the box, Odoo shows customers their sales orders, invoices, delivery orders, and project tasks here.

As a developer, you can extend the portal to show records from your own custom models — for example, library loan history, rental agreements, or support cases.

Portal users have a restricted security group (base.group_portal) that gives read access to their own records only. They cannot access the backend (/web/) at all.

Portal vs Website vs Backend

AreaURL prefixAuthWho uses it
Backend/web/Internal userStaff, managers
Portal/my/Portal user (customer login)Customers with an account
Website/Public (no login needed)Anyone on the internet

A portal user logs in with their email and a password you send them. They cannot see other customers' records — record rules enforce this at the database level.

How Portal Works (Technical Overview)

The portal is built on Odoo's HTTP controller layer. The main moving parts are:

  • portal.mixin — a mixin you add to your model; it contributes the access_token field and helper methods.
  • CustomerPortal controller — a base controller in odoo/addons/portal/controllers/portal.py that you inherit to add your model's listing and detail pages.
  • QWeb templates — portal pages use t-call="portal.portal_layout" as a wrapper that provides the header, breadcrumbs, and sidebar.
  • Record rules — restrict portal users to their own records via ir.rule domain filters on base.group_portal.

portal.mixin

portal.mixin is an abstract model defined in odoo/addons/portal/models/portal_mixin.py. Adding it to your model gives you:

  • access_token — a UUID field stored in the database, used for share links that bypass authentication.
  • access_url — a computed field that calls _compute_access_url(); you must override this.
  • access_warning — a warning string shown when access is restricted.
Python
from odoo import models, fields
from odoo.addons.portal.models.portal_mixin import PortalMixin

class LibraryLoan(models.Model, PortalMixin):
    _name = 'library.loan'
    _description = 'Library Loan'

    def _compute_access_url(self):
        for loan in self:
            loan.access_url = f'/my/loans/{loan.id}'

You also need to declare portal.mixin as a dependency in your _inherit list or use models.Model alongside it:

Python
class LibraryLoan(models.Model):
    _name = 'library.loan'
    _inherit = ['portal.mixin', 'mail.thread']
    _description = 'Library Loan'

    member_id = fields.Many2one('res.partner', string='Member')
    book_id = fields.Many2one('library.book', string='Book')
    loan_date = fields.Date(string='Loan Date')
    return_date = fields.Date(string='Return Date')
    state = fields.Selection([
        ('active', 'Active'),
        ('returned', 'Returned'),
        ('overdue', 'Overdue'),
    ], default='active')

    def _compute_access_url(self):
        for loan in self:
            loan.access_url = f'/my/loans/{loan.id}'

Enabling Portal Access for a Model

Three things are required to make records visible in the portal:

  1. Add portal.mixin — as shown above.
  2. Add access rights for portal users — in your ir.model.access.csv, give base.group_portal read access (perm_read=1, all others 0).
  3. Add a record rule — ensure portal users can only read their own records.
CSV
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_loan_portal,library.loan portal,model_library_loan,base.group_portal,1,0,0,0
XML
<record id="rule_library_loan_portal" model="ir.rule">
  <field name="name">Library Loan: portal users see own loans</field>
  <field name="model_id" ref="model_library_loan"/>
  <field name="groups" eval="[(4, ref('base.group_portal'))]"/>
  <field name="domain_force">[('member_id', '=', user.partner_id.id)]</field>
</record>

Portal URL Structure

By convention, portal URLs follow this pattern:

  • /my/ — portal home page (list of all available sections)
  • /my/loans — listing page for your model
  • /my/loans/<int:loan_id> — detail page for a single record
  • /my/loans/<int:loan_id>?access_token=xxxx — shared link with access token

Pagination is also standard: /my/loans/page/<int:page>.

Advertisement
Key takeaways:
  • portal.mixin adds access_token and access_url to your model
  • Portal users are in base.group_portal — they can't access the backend
  • Record rules restrict portal users to their own records
  • You need access rights CSV + record rule + controller + templates to complete a portal integration

Interview Questions

  • What is the Odoo portal?
  • What is the difference between a portal user and an internal user?
  • What can portal users access?
  • What are the /my routes?
  • How is portal access secured?

FAQ

Does every customer automatically get portal access? +

No. You must explicitly grant portal access to a contact from the backend: open the contact, click Action → Grant Portal Access, and select the user. This creates a portal user account and sends them an invitation email.

Can a portal user edit records? +

Only if you grant write access in the CSV and build a form controller. By default portal users are read-only. For write operations use dedicated form pages (see Portal Forms).

What is the access_token used for? +

Access tokens allow unauthenticated sharing — a customer can forward a URL containing their token and the recipient can view the record without logging in. The token is a UUID generated per-record and can be reset.

Do I need the website module for portal? +

No. The portal (portal module) is separate from the website builder (website module). The portal works without website installed, though both share the same QWeb layout infrastructure.