Cal.com → Odoo CRM
1. What? — Definition and context
Section titled “1. What? — Definition and context”The Cal.com → Odoo CRM workflow automatically syncs Cal.com events with Odoo CRM. It manages the full appointment lifecycle: creation, cancellation, rescheduling and meeting end.
Handled events
Section titled “Handled events”| Cal.com event | Odoo action | Notification |
|---|---|---|
BOOKING_CREATED | Create opportunity | Message + iCal file |
BOOKING_CANCELLED | Mark as lost | Cancellation message |
BOOKING_RESCHEDULED | Update dates | Message + new iCal |
MEETING_ENDED | Increase probability | Meeting-end message |
2. Why? — Stakes and motivations
Section titled “2. Why? — Stakes and motivations”Problems solved
Section titled “Problems solved”| Problem | Without sync | With sync |
|---|---|---|
| Double entry | Manually create the opportunity after the meeting | Automatic |
| Missed follow-up | No reminder after the meeting | CRM stage updated |
| Scattered calendar | Cal.com appointments ≠ work calendar | iCal file sent |
| Untracked cancellations | Lose the cancellation info | Lead marked as lost |
Lead lifecycle
Section titled “Lead lifecycle”3. How? — Technical implementation
Section titled “3. How? — Technical implementation”Architecture
Section titled “Architecture”Flow: appointment creation
Section titled “Flow: appointment creation”Sample notification
Section titled “Sample notification”📅 New Cal.com appointment
👤 Client: Jean Dupont📧 Email: jean.dupont@example.com
🗓️ Date: Thursday 20 January 2026⏰ Time: 14:00 - 15:00📋 Type: Discovery call
🔗 Opportunity: https://odoo.guigpap.com/odoo/crm/123N8N configuration
Section titled “N8N configuration”Cal.com Trigger:
Events: - BOOKING_CREATED - BOOKING_CANCELLED - BOOKING_RESCHEDULED - MEETING_ENDEDSwitch Node (Route by Event):
Type: SwitchMode: RulesValue: {{ $json.triggerEvent }}
Rules: - Output 0: BOOKING_CREATED - Output 1: BOOKING_CANCELLED - Output 2: BOOKING_RESCHEDULED - Output 3: MEETING_ENDEDFind Opportunity (Odoo):
Resource: CustomCustom Resource: crm.leadOperation: Get ManyLimit: 1Filter: - Field: email_from Operator: = Value: {{ $json.attendees[0].email }}iCal generation
Section titled “iCal generation”const booking = $json;const attendee = booking.attendees[0];
const formatICalDate = (date) => { return new Date(date).toISOString() .replace(/[-:]/g, '').split('.')[0] + 'Z';};
const icsContent = `BEGIN:VCALENDARVERSION:2.0PRODID:-//N8N//Booking//ENBEGIN:VEVENTUID:${booking.uid}@cal.comDTSTART:${formatICalDate(booking.startTime)}DTEND:${formatICalDate(booking.endTime)}SUMMARY:Cal.com appointment - ${attendee.name}DESCRIPTION:Appointment with ${attendee.name}LOCATION:${booking.location || 'TBD'}STATUS:CONFIRMEDEND:VEVENTEND:VCALENDAR`;
return [{ json: { fileName: `appt-${attendee.name}.ics` }, binary: { calendar: { data: Buffer.from(icsContent).toString('base64'), mimeType: 'text/calendar' } }}];Note on Odoo types
Section titled “Note on Odoo types”# Correct configurationFields: active: {{ false }} # Expression mode! probability: {{ 0 }} # Expression mode!4. What if? — Outlook and limits
Section titled “4. What if? — Outlook and limits”Current limits
Section titled “Current limits”| Limit | Impact | Mitigation |
|---|---|---|
| Email-based matching | Duplicates if emails differ | Future broader search |
| No reverse sync | Odoo → Cal.com not implemented | Telegram actions |
| Single attendee | Multi-participants not handled | Planned if needed |
Evolution scenarios
Section titled “Evolution scenarios”If reverse sync is needed:
- Webhook Odoo → N8N when an opportunity is updated
- Update the Cal.com booking
- Watch out for infinite loops
If multi-type appointments:
- Create different opportunity types depending on the Cal.com type
- Auto-assign by type
- Differentiated initial probability
If appointment volume grows:
- Daily digest instead of individual notifications
- Filter by appointment type
- Round-robin assignment
Troubleshooting
Section titled “Troubleshooting”| Problem | Check |
|---|---|
| No trigger | Cal.com: webhook configured? N8N: workflow active? |
| Opportunity not found | Attendee email = email_from in Odoo? |
| Update fails | Check Odoo credential permissions |
| iCal not generated | Generate iCal node connected? Binary data present? |
Related pages
Section titled “Related pages”Workflows
Section titled “Workflows”- Notification Hub — Centralised routing
- Website Lead Notification — Web-form leads
Infrastructure
Section titled “Infrastructure”- Odoo 18 on Docker — ERP and CRM