Connect Custom HTML Form
to Odoo CRM
Fix the CSRF “Session Expired” error for good. A complete step-by-step guide to connecting any custom-designed HTML form to Odoo CRM — so leads flow directly into the backend using a JavaScript bridge.
Why Direct Form Submission Fails in Odoo
Understanding the CSRF protection that causes the “Bad Request — Session Expired” error.
Odoo’s website forms have a security system called CSRF (Cross-Site Request Forgery protection).
This means only forms that Odoo itself creates can send data to the CRM.
If you try to send data from a custom HTML form directly, Odoo rejects it with a “Bad Request — Session Expired” error.
The solution: Keep Odoo’s native form on the page (hidden from users),
and use JavaScript to copy data from your custom form into Odoo’s hidden form —
then trigger Odoo’s own submit button. Odoo thinks it’s its own form submitting, so it accepts it perfectly.
Step 1 — Create the Odoo Native Form
Drag and drop the Odoo Form block into the page using the website editor.
Go to your Odoo website. Navigate to the page where the custom form already exists. Click the Edit button (top right) to enter the website editor.
In the left sidebar, click BLOCKS. Scroll down until you see the “Form” block (it has a form icon). It is usually near “CTA Badge”, “Blockquote” and “Countdown”.
Click and drag the Form block. Drop it anywhere on the page — it does not matter where because we will hide it with CSS later. Drop it just below the section that has the custom form.
Click on the form you just dropped. In the right panel under CUSTOMIZE, find the Action dropdown. It will say “Send an E-mail” by default.
Click it and look for “Create a Lead” or type crm.lead in the search box. Select it.
Step 2 — Configure Fields & CRM Mapping
Set up which fields you need and map them to the correct CRM fields in Odoo.
Look at the custom HTML form on the page. Note down every field it has. For example:
- Name input field
- Phone number input
- Dropdown (interest/event type)
The Odoo native form needs to have at least the same fields so data can be copied across using JavaScript.
Click on a field in the Odoo form to select it. In the right panel you will see its settings — Type, Label, Visibility.
To add a field, click the + Field button at the top of the right panel. A dropdown will appear showing all CRM fields available — scroll and select the one you need.
To delete a field you do not need, select it and press the trash icon in the panel.
| Custom Form Field | Odoo CRM Field to Map | Technical Name | Status |
|---|---|---|---|
| Name | Subject / Lead Name | name |
Required |
| Phone | Phone Number | phone |
Optional |
| Interest / Event Type | Event Type (custom studio field) | x_studio_event_type |
Optional |
| (not shown to user) | email_from |
Hidden — set default |
Some fields like Email are required by Odoo but we do not want to show them to users. To hide them:
- Click the field (e.g. Email)
- In the right panel, find Visibility and set it to “Hidden”
- Then set a default value — e.g.
noreply@medkon.in— so the field is not empty on submit
Click Save in the top bar. This is important — the Odoo form field IDs are only stable after saving. If you skip this step, the IDs you collect in Step 3 may change.
Step 3 — Get Odoo Field IDs via Browser Console
We need the exact HTML IDs of each Odoo form field to target them with JavaScript.
Go to the live URL of the page. Make sure you are NOT in the website editor — click Preview or open the URL directly in a new tab.
Press F12 (or right-click anywhere → Inspect). Click the Console tab. Paste this command and press Enter:
document.querySelectorAll('form[action="/website/form/"] input, form[action="/website/form/"] select, form[action="/website/form/"] textarea').forEach(function(el) { console.log(el.name, '|', el.id, '|', el.type); })
You will see output like this:
name | owude1wmma9 | text phone | ovpkeyzraqc | tel x_studio_event_type | o8alwh71bxhb | text email_from | o382en45sli4 | email
Copy this output into a notepad. You will need the IDs (the middle column) in the next step to build the JavaScript bridge.
owude1wmma9 for each form. If you edit and re-save the form in the editor, the IDs may change. Always re-run this command after any edits.
Step 4 — Write the Bridge JavaScript
This script reads data from the custom form and pushes it into Odoo’s hidden form, then clicks Odoo’s native submit button to bypass CSRF.
In Odoo website editor, click on the section that contains your custom HTML. Click Edit in HTML editor or the code icon (</>) to open the raw HTML.
Find the <script> block in your custom HTML. Replace the existing submit handler with this template — filling in the IDs from Step 3:
// ── Bridge: Custom Form → Odoo CRM ── var form = document.getElementById('YOUR_CUSTOM_FORM_ID'); var successBox = document.getElementById('YOUR_SUCCESS_DIV_ID'); if (form) { form.addEventListener('submit', function(e) { e.preventDefault(); // 1. Read values from your custom form fields var name = document.getElementById('YOUR_NAME_INPUT_ID').value.trim(); var phone = document.getElementById('YOUR_PHONE_INPUT_ID').value.trim(); var interest = document.getElementById('YOUR_SELECT_ID').value; // 2. Basic validation if (!name || phone.length < 10 || !interest) { alert('Please fill all fields correctly.'); return; } // 3. Copy values into Odoo's hidden native form // Use the IDs from the browser console (Step 3) document.getElementById('ODOO_NAME_FIELD_ID').value = name; document.getElementById('ODOO_PHONE_FIELD_ID').value = phone; document.getElementById('ODOO_INTEREST_FIELD_ID').value = interest; // 4. Click Odoo's native submit button — handles CSRF automatically document.querySelector('.s_website_form_send').click(); // 5. Show success message, hide custom form form.style.display = 'none'; if (successBox) successBox.style.display = 'block'; }); }
ODOO_NAME_FIELD_ID → owude1wmma9ODOO_PHONE_FIELD_ID → ovpkeyzraqcODOO_INTEREST_FIELD_ID → o8alwh71bxhb
If the page has more than one Odoo native form, document.querySelector('.s_website_form_send') will click the first one it finds. To target the correct one, give the Odoo submit button a unique ID in the HTML editor and use document.getElementById('your-unique-id').click() instead.
Step 5 — Hide the Odoo Native Form with CSS
The Odoo form must exist in the DOM but should be invisible to users.
In the Odoo website editor, go to Customize → Edit CSS. Add this single line:
.s_website_form { display: none !important; }
display: none hides the Odoo form visually but it still exists in the HTML. JavaScript can still find it, fill its fields, and click its submit button. The user never sees it.
Step 6 — Test & Verify in CRM Backend
Always test with a dummy entry before going live or handing off.
On the live page, fill in your custom form with test data:
- Name: Test Lead
- Phone: 9999999999
- Interest: Select any option
Click submit. The success message should appear and the form should hide.
Go to CRM → Leads in the Odoo backend. The new lead should appear at the top with the name, phone, and event type you entered.
After confirming everything works, go to CRM and delete the test lead so it does not pollute your real pipeline data.
Quick Reference — Odoo CRM Field Names
Common Odoo CRM field technical names you will encounter when adding fields to the native form.
| What it stores | Odoo field name (name= attribute) | Notes |
|---|---|---|
| Lead title / Subject | name |
Required by Odoo. Used as the lead title in CRM. |
| Contact name | contact_name |
The person’s name shown in CRM lead card. |
| Phone number | phone |
Standard phone field. |
email_from |
Often hidden with a default value like noreply@yourdomain.com | |
| Company name | partner_name |
Used for B2B / corporate forms. |
| Description / Notes | description |
Free text notes on the lead record. |
| Event Type (custom) | x_studio_event_type |
Custom field created in Odoo Studio. All Studio fields are prefixed with x_studio_. |
| Follow Up Notes (custom) | x_studio_notes_3 |
Custom studio field. Exact name varies per Odoo account. |
Use AI to Generate the Bridge JS for Complex Forms
When a form has more than 3 fields, use this AI prompt template instead of writing the JavaScript manually.
Whenever a form has more than 3 fields, or the form is complex (dropdowns, checkboxes, multiple sections), writing the JS manually is error-prone. Give AI your custom form HTML and the Odoo field IDs — it will generate the correct bridge JS instantly.
- Your custom form HTML — copy the entire <form>…</form> block from the HTML editor in Odoo
- Odoo field IDs output — the console output from Step 3 (the name | id | type list)
Open Claude (claude.ai) or any AI assistant. Paste this prompt — replacing the two sections marked with [ ]:
I have a custom HTML form on an Odoo website page. I also have an Odoo native form hidden on the same page that connects to Odoo CRM. My goal: When the user submits MY custom form, JavaScript should: 1. Read the values from my custom form fields 2. Copy those values into the matching Odoo native form fields 3. Click the Odoo native submit button (.s_website_form_send) 4. Hide my custom form and show a success message div Here is my CUSTOM FORM HTML: [PASTE YOUR CUSTOM FORM HTML HERE] Here is the Odoo native form field list (from browser console): [PASTE THE CONSOLE OUTPUT HERE — the name | id | type list] Please write the complete bridge JavaScript for this. Map each custom form field to its corresponding Odoo field by name. Include basic validation (required fields must not be empty). The success div ID is: [YOUR SUCCESS DIV ID]
AI will return a complete JavaScript block. Here is how to use it:
- Copy the JS code AI gives you
- In Odoo website editor, open the HTML block that has your custom form
- Find the existing
<script>tag with the old form submit handler - Replace the old handler with the new AI-generated code
- Save the page and test
Final Checklist Before Going Live
Go through each point before marking the task as done.
- Odoo native form is on the page and connected to CRM (action = crm.lead)
- All required fields in Odoo form are either visible or hidden with a default value
- Field IDs collected from browser console and noted down
- Bridge JavaScript correctly maps custom form IDs → Odoo field IDs
- CSS hides the Odoo form section (
display: none) - Test submission done — lead appeared in CRM backend
- Test lead deleted from CRM after verification
- Success message appears and form hides after submission
- Page tested on mobile device
Frequently Asked Questions
Common questions about connecting custom HTML forms to Odoo CRM.
.s_website_form_send is the CSS class Odoo assigns to the native form’s submit button. In the JavaScript bridge, you call document.querySelector('.s_website_form_send').click() to programmatically submit the Odoo form. Because this is Odoo’s own button, the CSRF token is included automatically and the submission goes through without errors.
owude1wmma9) for each form field when you save the page in the website editor. Every time you save the page, these IDs can regenerate. This is why you must always re-run the browser console command after editing the Odoo native form — to get the latest IDs before updating your JavaScript bridge.