MailDesk docs
Get MailDesk
Technical

Model reference

This is the per-model reference for the two MailDesk apps: Basic (maildeskmailclient) and Pro (maildeskmailclientpro). It covers the models each app defines (name) and the core Odoo models each app extends (inherit). For the synchronization data-flow that connects maildesk.messageindex → maildesk.uicache → maildesk.ingestqueue → mail.thread, see Architecture; for the outbound maildesk.updatequeue path see Realtime architecture.

9 min read

This is the per-model reference for the two MailDesk apps: Basic (maildesk_mail_client) and Pro (maildesk_mail_client_pro). It covers the models each app defines (_name) and the core Odoo models each app extends (_inherit). For the synchronization data-flow that connects maildesk.message_indexmaildesk.ui_cachemaildesk.ingest_queuemail.thread, see Architecture; for the outbound maildesk.update_queue path see Realtime architecture.

Conventions. "Providing module" is the addon whose models/ defines or extends the model. Field types are abbreviated (M2o = Many2one, M2m = Many2many, O2m = One2many, Sel = Selection). Identity column = delivery_key shorthand for (account, provider, folder, uid).


1. Basic — maildesk_mail_client

The Basic app is the synchronization engine and the data owner. It defines the SSOT and all of the sync/state/cache infrastructure, plus a small set of extensions on native Odoo mail models.

1.1 Models defined by Basic (_name)

Model Purpose Key fields Key methods Relations Source file
maildesk.message_index SSOT — authoritative metadata row for every synced message. Identity = delivery_key. _order = "sort_ts desc, id desc" account_id, provider (Sel imap/gmail/outlook), folder, uid, message_id, from_addr, to_addrs/cc_addrs/bcc_addrs, subject, date, preview, body_text, has_attachments, is_read, is_starred, flags, tag_ids, thread_id, sort_ts, deleted_on_server, ingest_allowed, pending_move_to, pending_delete, local_pending upsert_one, upsert_one_with_inserted, update_state_projection, update_tags, find_by_triplet, confirm_outbound_delivery, reconcile_thread_id_from_reply, delete_or_tombstone, store_search_body_text, init (creates trigram indexes) account_idmailbox.account; tag_idsmail.message.tag (M2m) models/message_index.py
mailbox.account A configured Gmail/Outlook/IMAP mailbox: credentials, OAuth state, sync watermarks, ACL. _order = "sequence, name" name, email, password, sender_name, mail_server_id, mail_send_server_id, imap_caps (Json), gmail_last_history_id, gmail_last_sync_at, outlook_delta_link, outlook_delta_tokens (Json), outlook_graph_refresh_token/_access_token/_auth_state, backoff_until, consecutive_failures, needs_admin_attention, owner_id, company_id, is_shared, signature (Html); is_outlook/is_gmail (computed) button_test_incoming/button_test_outgoing, action_setup_gmail/_outlook/_imap, action_auth_outlook_graph, action_reset_mailbox_sync_state, get_account_list, get_folder_tree, get_message_with_attachments, poll_mail_accounts, refresh_imap_caps, _get_imap_client folder_idsmailbox.folder (O2m); owner_idres.users; company_idres.company; mail_server_id/mail_send_server_idir.mail_server/fetchmail.server models/mailbox_account.py
mailbox.folder Per-account folder with bootstrap/backfill progress and provider-specific cursors. _order = "account_id, sequence, name" name, imap_name, account_id, parent_id, is_visible, uid_validity, last_uid, last_uidnext, last_highest_modseq, needs_sync, sync_state (Sel), bootstrap_attempts, backfill_started_at/_completed_at/_completion_reason/_total_estimate/_fetched_count/_last_uid, gmail_label_id, gmail_backfill_page_token, outlook_graph_id, outlook_backfill_next_link, unread_count, folder_type (Sel); backfill_progress/backfill_status_display (computed) sync_folders_for_account, bootstrap_if_pending, progressive_backfill, trigger_backfill, action_reset_bootstrap, get_folder_tree, _try_acquire_folder_sync_lock (advisory lock ns 48232), _sync_outlook_graph account_idmailbox.account; parent_id/child_ids self-referential models/mailbox_folder.py
maildesk.ui_cache TTL cache for message bodies/attachment lists, guarded by PostgreSQL advisory locks (ns 48231). _order = "id desc" index_id, json_cache (Json), json_cache_until, cache_until acquire_index_lock, acquire_message_lock, acquire_cache_lock_for_index, resolve_canonical_index_id, fetch_for_index_records, upsert_list_cache, fetch_body_cache/upsert_body_cache, invalidate_message_cache, prepare_reply/prepare_forward, _cron_cleanup_expired_cache index_idmaildesk.message_index models/maildesk_ui_cache.py
maildesk.ingest_queue Durable queue that materialises SSOT rows into Odoo's native mail.thread. _order = "priority asc, id asc" index_id, state (Sel), retry_count, next_try_at, error_message, raw_body (Binary), processed_at/processed_model/processed_res_id, priority, started_at cron_scan_aliases, cron_import, cron_recover_stuck, action_requeue, action_clear_raw_body index_idmaildesk.message_index models/ingest_queue.py
maildesk.email_state Per-message read/star/move/delete intent, projected onto SSOT rows. _order = "write_date desc" account_id, folder, uid, seen/has_seen, starred/has_starred, target_folder/has_move, dirty_flags/dirty_move, is_delete/has_delete, expire_at record_flags, record_delete, record_move, apply_overrides_to_records, cron_gc_states, _feature_enabled account_idmailbox.account models/maildesk_email_state.py
maildesk.draft Composer drafts (incl. provider-sync state for Gmail/Outlook draft mirroring). _order = "write_date desc, id desc" user_id, account_id, subject, body_html (Html, sanitize=False), to_emails/cc_emails/bcc_emails, attachment_ids (M2m), tag_ids (M2m), reply_to_message_id, model/res_id, request_read_receipt/request_delivery_receipt, message_id, provider_type, provider_draft_id, provider_message_uid, provider_synced_at, provider_sync_error (CRUD via mailbox.sync: save_draft/update_draft/load_draft/delete_draft) user_idres.users; account_idmailbox.account; attachment_idsir.attachment models/maildesk_draft.py
maildesk.account_lease Cooperative sync lock per account (TTL-based) so only one worker syncs an account at a time. _order = "lease_until desc" account_id, lease_until, owner try_acquire, renew, release, reap_expired, _compute_lease_until account_idmailbox.account models/maildesk_account_lease.py
maildesk.ui_presence Lightweight per-user "online in MailDesk" ping for presence-aware bus broadcasts. _rec_name = "user_id" user_id, last_ping_at ping, online_user_ids user_idres.users models/maildesk_ui_presence.py
mail.message.tag User-defined coloured tag applied to indexed messages name (translate), color get_tag_list referenced by maildesk.message_index.tag_ids (M2m) models/mail_message_tag.py
mailbox.sync RPC service façade (no persistent rows of its own) — the frontend's main backend entry point for listing, opening, flagging, moving, drafting, sending — (transient-style service model) message_search_load, poll_for_updates, get_message_with_attachments, fetch_thread_messages/open_thread_messages, set_flags/set_flags_bulk, move_messages_to_folder, update_tags, save_draft/update_draft/load_draft/delete_draft, send_email, delete_messages, unread_counts_for_folder, _check_account_access calls into mailbox.account, maildesk.message_index, maildesk.ui_cache, maildesk.email_state, maildesk.draft models/mailbox_sync.py

1.2 Native Odoo models extended by Basic (_inherit)

Model What Basic adds Source file
mail.message account_id (M2o mailbox.account), recipient_cc_ids / recipient_bcc_ids (M2m res.partner) models/mail_message.py
mail.mail account_id (M2o mailbox.account) so outbound mail routes through the right mailbox / SMTP server and sender_name models/mail_mail.py
mail.thread MailDesk-specific thread hooks models/mail_thread.py
ir.attachment MailDesk attachment handling models/ir_attachment.py
ir.websocket Account-scoped bus channel subscription (in-app freshness) models/ir_websocket.py
ir.mail_server / fetchmail.server MailDesk SMTP/IMAP server linkage models/ir_mail_server.py, models/fetchmail_server.py
google.gmail.mixin / microsoft.outlook.mixin OAuth2 token handling for Gmail / Graph models/google_gmail_mixin.py, models/microsoft_outlook_mixin.py
res.company Per-company OAuth credentials: google_gmail_client_identifier/_secret, microsoft_outlook_client_identifier/_secret (with legacy global-param fallback) models/res_company.py
res.partner trusted_partner, trusted_by_user_id, maildesk_email_count (computed); methods trust_sender, email_history, get_partners_with_email_activity, search_unique_partners_by_email models/res_partner.py
res.config.settings MailDesk config-parameter surface models/res_config_settings.py

License guard (cross-cutting). models/mail_thread_ext.py injects an access hook on the module's concrete models so that ORM access is gated by the Metzler IT licensing layer. The guard is inert in automated-test / CI contexts. Developers should be aware the hook exists when extending models.


2. Pro — maildesk_mail_client_pro

Pro extends Basic with bidirectional (outbound) mutations, the asynchronous AI subsystem, attachment extraction, signatures, and the email→record link contract. Pro requires Basic.

2.1 Models defined by Pro (_name)

Two-way sync & linking

Model Purpose Key fields Key methods Relations Source file
maildesk.update_queue Outbound mutation queue. One coalesced row per delivery for flag/label/move/soft-delete; lease-locked, backoff, max_attempts default 8. _order = "priority asc, id asc" account_id, provider (Sel imap/gmail/outlook), folder, uid, email_state_id, state (Sel pending/in_progress/done/failed/skipped), attempt_count, max_attempts (default 8), next_try_at, last_error, priority, locked_by/locked_until, started_at/finished_at ensure_queued (coalescing upsert), cron_process (batch 80, 15 s budget), cron_cleanup_update_queue account_idmailbox.account; email_state_idmaildesk.email_state; consumed by per-provider */mutation_executor.py models/maildesk_update_queue.py
maildesk.email.link The shared contract linking an external email (SSOT row) to an Odoo record. Bridges (CRM/Helpdesk/Sale/Documents/Calendar) declare link_type semantics on this one field. _order = "create_date desc" message_index_id, model, res_id, link_type (Sel — base only generic; bridges extend), display_name (computed/stored), user_id copy_links_to_message, _maildesk_target_exists, _maildesk_sync_integration_pointers, _compute_display_name message_index_idmaildesk.message_index; model/res_id are a soft (non-FK) reference to any Odoo record models/maildesk_email_link.py
mailbox.signature Reusable email signatures, personal or shared per mailbox, with default selection. _order = "is_shared desc, is_default desc, sequence, name, id" name (translate), account_id, user_id, is_shared, is_default, sender_name, body_html (Html); preview_text (computed) get_signature_picker_data, resolve_signature, _maildesk_validate_scope/_validate_user_access, _maildesk_sync_default_flags account_idmailbox.account; user_idres.users models/mailbox_signature.py

AI subsystem

API keys are never stored on these rows — they live only in ir.config_parameter (maildesk.ai.<provider>_key). Insight models are explicitly "derived-only": they store summaries and structured facts, never raw email bodies. See AI architecture and the single authorization chokepoint application/services/ai_guard.py.

Model Purpose Key fields Key methods Relations Source file
maildesk.ai.provider A configured AI backend (the row is metadata only — the key lives in config params). _order = "sequence, id" name, provider (Sel: openai/gemini/anthropic/grok/deepseek/custom), model, base_url, is_active, api_key_set (computed), connectivity_status (Sel) action_set_api_key, action_test_connectivity, action_activate, get_active_status, get_task_status, _check_custom_base_url, _sync_active_provider per-task routing via config param maildesk.ai.task.<task>.provider models/maildesk_ai_provider.py
maildesk.ai.job Async enrichment job (the work queue that produces insights). _order = "priority asc, id asc" account_id, subject_type (Sel message/thread/attachment/search/partner), index_id, thread_key, attachment_id, search_token, task_key (Sel: message_brief/priority_score/thread_digest/reply_suggest/record_action_suggest/attachment_extract/attachment_qa/search_rerank/partner_digest), provider_key, model_name, fingerprint, dedup_key, payload_json/result_json, state (Sel), attempt_count/max_attempts (8), prompt_tokens/completion_tokens schedule (dedup-aware), cron_ai_jobs, cron_cleanup_ai_jobs, _compute_dedup_key, _insight_is_fresh account_idmailbox.account; index_idmaildesk.message_index models/maildesk_ai_job.py
maildesk.ai.message.insight Per-message derived insight (priority, action items, suggested tags/route/record actions). _rec_name = "summary_short" index_id, account_id, summary_short, priority_level (Sel)/priority_reason, action_items_json, suggested_tags_json, suggested_route_json, suggested_record_actions_json, reply_intent, detected_language, confidence, insight_hash, provider_key/model_name, token counts, refreshed_at upsert, fetch_fresh, is_stale, fetch_for_index_ids, to_ui_dto index_idmaildesk.message_index; account_idmailbox.account models/maildesk_ai_message_insight.py
maildesk.ai.thread.insight Per-thread derived insight (long summary, delta, open questions, commitments, next actions) account_id, thread_key, summary_long, delta_since_last_user_reply, open_questions_json, commitments_json, next_actions_json, participants_json, suggested_record_actions_json, latest_processed_sort_ts, message_count, latest_index_id, insight_hash, token counts, refreshed_at upsert, fetch_fresh, fetch_any, is_stale, to_ui_dto account_idmailbox.account models/maildesk_ai_thread_insight.py
maildesk.ai.conversation "Ask AI" panel conversation, mailbox-shared (a conversation with no mailbox stays private to its creator). _order = "last_used_at desc, id desc" user_id, account_id, thread_key, title, last_used_at get_or_create, touch, cron_cleanup_conversations account_idmailbox.account; message_idsmaildesk.ai.conversation.message (O2m) models/maildesk_ai_conversation.py
maildesk.ai.conversation.message One turn of an Ask-AI conversation (author-stamped). _order = "id asc" conversation_id, role (Sel user/assistant), author_id, content_text, citations_json, token counts append, to_ui_dto conversation_idmaildesk.ai.conversation; author_idres.users models/maildesk_ai_conversation_message.py
maildesk.ai.attachment.extract Opt-in attachment extraction result (text excerpt + structured facts, keyed by checksum). _order = "id desc" index_id, account_id, attachment_ref, checksum, mime_type, file_size, status (Sel), extract_excerpt, structured_facts_json, citations_json, extract_hash, provider_key/model_name, refreshed_at upsert, fetch, to_ui_dto index_idmaildesk.message_index; account_idmailbox.account models/maildesk_ai_attachment_extract.py

Integration & handler models

Model Purpose Source file
maildesk.chatter Lazy "discussable" creation + write-back that hydrates MailDesk threads into a record's Chatter models/maildesk_chatter.py
maildesk.post.send.handler.composer_link Post-send handler that re-links a composed reply to the originating Odoo record models/post_send_handler.py

2.2 Native / Basic models extended by Pro (_inherit)

Model What Pro adds Source file
mailbox.account Per-mailbox AI gate + tuning: allow_ai (Boolean, default True), allow_ai_attachments (Boolean, default False), ai_team_context (Text), ai_reply_tone (Sel: professional/formal/friendly/technical/concise, default professional), ai_brief_activated_at (brief watermark), maildesk_inbox_realtime_sync_at (near-realtime Inbox watermark). Note: there is no ai_enabled field — the per-mailbox gate is allow_ai. models/mailbox_account.py
maildesk.message_index Pro outbound/linking extensions on the SSOT models/maildesk_message_index.py
maildesk.ui_cache Pro cache extensions models/maildesk_ui_cache.py
mailbox.folder / mailbox.sync Pro outbound-mutation + AI hooks models/mailbox_folder.py, models/mailbox_sync.py
maildesk.bus_orchestrator Pro bus orchestration extensions models/maildesk_bus_orchestrator.py
mail.thread / mail.template / ir.model Pro integration hooks models/mail_thread.py, models/mail_template.py, models/ir_model.py
res.config.settings AI provider + per-task routing settings surface models/res_config_settings.py

access_user_ids. Per-mailbox membership (access_user_ids on mailbox.account) is the field every record rule keys on for shared-inbox isolation. The field exists on the Basic mailbox.account model (used directly by the Basic record rules); Pro relies on it heavily for AI and outbound scoping. See Security.

4.2.0 (branch-only, not merged). The realtime/prefetch work adds maildesk.message_prefetch_queue and maildesk.push_subscription on branches MailDesk-889-realtime-sync-v18 / MailDesk-prefetch-cache-v18 (MRs !237 Basic, !124 Pro). Those models are not in shipped 4.1.x and are documented in Realtime architecture §2, not above.