EVERJUST Website Events — Agent Skill
Build and publish the public event site of a live everjust.app tenant as an agent: turn an
event.event into a browsable minisite at /event/<slug> (with its Home / Practical / Register /
Agenda / Talks / Exhibitors / Propose-a-talk sub-pages), publish the agenda (event.track
sessions — speakers, times, rooms), publish the sponsor / exhibitor wall (event.sponsor by
tier), and drive online registration (event.registration) through the website register route —
all through the Odoo MCP / ORM (search, get, create, update, call, describe_model) plus
the MCP WEBSITE TOOLS where site-chrome is involved. See [[everjust-agent-mcp]] for opening the
session against the right tenant DB.
The crucial everjust facts:
- This is STOCK Odoo 19 —
website_event (the minisite + register route), website_event_track
(event.track agenda + speakers + track_proposal form), website_event_exhibitor
(event.sponsor = the sponsor/exhibitor wall), website_event_crm (registrations → crm.lead).
There is no custom everjust event addon. Read fields from the live model (describe_model /
fields_get), not from memory of an older Odoo.
- The event minisite is GENERATED, not authored. The
website_event controller renders
/event/<slug> and its sub-pages from the event's records + per-event menu toggles — you do
NOT create a website.page per event or per track. The WEBSITE TOOLS
(website_new_page/website_edit_page) are for the STATIC /event list chrome and marketing
pages, not for the generated minisite (Pitfall 1). You "build the site" by writing records
(event.event toggles, event.track, event.sponsor) and flipping is_published.
- Per-event MENU TOGGLES decide which sub-pages exist. Boolean fields on
event.event
(website_menu master, plus register_menu, website_track, website_track_proposal,
exhibitor_menu, community_menu, introduction_menu) each auto-create/remove a
website.event.menu row (a website.menu entry under the event) — that is how the "Agenda",
"Talks", "Exhibitors", "Register", "Propose a talk" tabs appear. Flip the boolean; don't hand-make
the menu rows (Pitfall 4).
event.track / event.sponsor exist on ONLY SOME tenants. website_event_track and
website_event_exhibitor are installed on tcstartupweek but uninstalled on
connectdomain / headsup / weldon — there, env["event.track"] / env["event.sponsor"]
raise KeyError. Check install state first (recipe 0). This mirrors [[everjust-events]]'s matrix.
- Track visibility has a THREE-part gate, not one flag (recipe 2 / Pitfall 5): the track's
stage_id must be a public/agenda stage, AND is_published True, AND the event's website_track
menu on. Publishing a track is NOT just is_published.
- The TCSW MARKETING pages are a SEPARATE hand-authored Tailwind QWeb layer.
/tickets and
/sponsor on tcstartupweek are pixel-faithful Tailwind website.page QWeb (module website_tcsw)
with hardcoded tier cards that merely link to /event/<slug>/register — they are NOT rendered
from event.sponsor / ticket records. Editing them is the site-chrome path
([[everjust-platform]] / website_edit_page), NOT this skill; this skill is the generated event
minisite + its content records (Pitfall 2).
- Registration on these tenants is FREE RSVP.
event_sale / website_event_sale are
uninstalled everywhere — the website register form creates a free event.registration; there is
no paid checkout / sale.order. Ticket types (event.event.ticket) are free segmentation only.
You reach the ORM + WEBSITE TOOLS through the platform's Odoo MCP — see [[everjust-agent-mcp]] for
opening the session against the right tenant DB and the tool signatures / confirm + ACL gates.
Every recipe below is expressed through that toolset. Website edits (publishing, menu toggles,
editing chrome) require the connected user to have website-designer / admin rights — check
can_publish first (recipe 0).
When to use this skill
- Publish / unpublish an event minisite — flip an
event.event's is_published (+ set
website_visibility) so it appears at /event, and choose which sub-pages exist via the menu
toggles (website_menu, register_menu, website_track, exhibitor_menu, …).
- Build / publish the agenda — create or publish
event.track sessions (name, date,
duration, location_id/room, stage_id, speaker partner_* fields, tag_ids), so the
Agenda/Talks pages render them.
- Manage the sponsor / exhibitor wall — create or publish
event.sponsor records (a partner_id
at a sponsor_type_id tier, exhibitor_type sponsor/exhibitor/online, logo, url,
website_description), so the Exhibitors page and sponsor footer render them by tier.
- Enable / read online registration — turn on the Register page, point people at the website
register route, and read the
event.registrations it produced (registration mechanics, CRM leads,
confirmation/reminder comms are the BACKEND — [[everjust-events]]).
- Tune an event page's presentation — cover image (
cover_properties), subtitle,
SEO/OpenGraph (website_meta_*, seo_name), website_visibility (public / link-only / logged-in).
- Diagnose — an event not on
/event (unpublished / visibility / wrong website_id), an Agenda
or Exhibitors tab missing (menu toggle off, or the add-on not installed), a track not showing
(stage/is_published), a sponsor not on the wall (is_published).
Do NOT use this skill for, and stop if the task is really:
- The registration/CRM/reminder BACKEND. Creating/confirming/cancelling an
event.registration
by hand, wiring event.lead.rule (registrations → crm.lead), scheduling event.mail /
event_sms confirmations & reminders, seats logic — all that is [[everjust-events]]. THIS skill is
the public site: exposing the event, agenda, sponsors, and the register page; it reads
registrations but does not run the registration lifecycle.
- The static
/event LIST chrome or a MARKETING page. The /event index styling and the TCSW
/tickets / /sponsor Tailwind pages are website.page / ir.ui.view QWeb — edit them with the
WEBSITE TOOLS keeping the existing Tailwind idiom; that's the site-chrome path in
[[everjust-platform]], NOT the generated minisite. Don't confuse hardcoded marketing tier cards
with event.sponsor records.
- Blog posts —
blog.post / website_blog; see [[everjust-website-blog]]. An event is not a
blog post (different publish gate).
- Installing
website_event_track / website_event_exhibitor on a tenant that lacks them — that
is platform-ops (module install), not this skill. On a tenant without the add-on the agenda /
exhibitor models simply don't exist; stop.
- Booths / paid tickets / live streaming / meet —
event_booth*, event_sale /
website_event_sale, website_event_track_live, website_event_meet are uninstalled on every
everjust tenant at last check. There is no booth booking, paid checkout, or live-stream page.
Where it's installed (verified live, 2026-07)
One tenant DB per customer; the event-website add-ons install with the tenant's public-website theme.
Verified live via ir_module_module.state (and consistent with [[everjust-events]]):
| Tenant DB |
website_event (minisite + register) |
website_event_track (event.track) |
website_event_exhibitor (event.sponsor) |
website_event_crm |
tcstartupweek |
✅ |
✅ |
✅ |
✅ |
connectdomain |
✅ |
uninstalled |
uninstalled |
✅ |
headsup |
✅ |
uninstalled |
uninstalled |
(event) |
weldon |
✅ |
uninstalled |
uninstalled |
(event) |
event_sale, website_event_sale, event_booth*, website_event_booth*,
website_event_track_live, website_event_meet were uninstalled on all at last check. So the
agenda + sponsor wall only exist on tcstartupweek; the other tenants get the event minisite +
register page only. Always confirm on your target DB (recipe 0) — env["event.track"] /
env["event.sponsor"] KeyError where the add-on isn't installed. On tcstartupweek at last check:
7 events (2015-2026), and only the 2026 event (id 1) is published (is_published=True,
website_visibility='public') — it carries 12 published event.track sessions and its track +
register + track_proposal menus on, against **1066 total tracks + ~52 sponsors** (most on the
older, unpublished archive events, and most sponsors not yet published on the 2026 event). Treat
these counts as volatile — read them live, don't hard-code them.
Architecture (the model map)
Everything is per-tenant DB, per-company_id, and per-website_id. Real fields below are from
fields_get on the live tcstartupweek DB (Odoo 19). Read fields live — don't assume older shapes.
| Model |
Role |
Key fields (real, confirmed live) |
event.event |
The event = one minisite. Its booleans decide which sub-pages exist; is_published puts it on /event. |
name, subtitle, date_begin/date_end* (Datetime, UTC-naive), date_tz* (display tz — required), seats_limited/seats_max/seats_available/seats_taken (computed), organizer_id/address_id (→res.partner), stage_id (→event.stage), is_published (bool — on /event list), website_published (computed mirror — read, don't write), can_publish (computed — may YOU publish), is_visible_on_website (computed), website_visibility (public|link|logged_users), website_id (→website; False=all sites), website_url (/event/<slug>), event_register_url (computed register link), menu_id (→website.menu, the event's root menu), menu toggles → website_menu (master), register_menu, community_menu, introduction_menu, website_track, website_track_proposal, exhibitor_menu (booleans; each spawns a website.event.menu), the matching *_menu_ids (→website.event.menu, read-only), cover_properties (JSON text cover, same shape as blog), ticket_instructions (Html on register page), question_ids/general_question_ids/specific_question_ids (→event.question, register-form questions), event_ticket_ids (→event.event.ticket, free ticket types), track_ids/track_count (→event.track), sponsor_ids/sponsor_count (→event.sponsor), tag_ids (→event.tag), allowed_track_tag_ids/tracks_tag_ids (agenda tag filter), seo_name, website_meta_title/website_meta_description/website_meta_keywords/website_meta_og_img |
website.event.menu |
One sub-page/tab of the minisite, auto-created by a menu toggle. Don't create by hand. |
event_id, menu_id (→website.menu), menu_type* (community|introduction|register|other|exhibitor|track|track_proposal), view_id (→ir.ui.view), seo_name, website_meta_*. (Labels are relabeled in the UI: introduction="Home", register="Practical", track="Talks"/"Agenda".) |
event.track |
One agenda session / talk (⚠️ website_event_track only). Has the speaker inline. |
name, event_id, stage_id* (→event.track.stage — the publish/agenda gate), is_published (bool), website_published (computed), can_publish (computed), date (Datetime, session start), date_end/duration (float hours), location_id (→event.track.location, the room/venue), tag_ids (→event.track.tag), color, priority, description (Html), speaker inline: partner_id (→res.partner), partner_name, partner_email, partner_phone, partner_function (job title), partner_company_name, partner_biography (Html), contact_email/contact_phone, website_cta/website_cta_title/website_cta_url (a call-to-action button on the track page), wishlisted_by_default, website_url (/event/<slug>/track/<track-slug>), seo_name, website_meta_*. (TCSW adds a studio field x_venue_partner_id.) |
event.track.stage |
Kanban stage that ALSO gates public agenda visibility. |
name*, sequence, fold, is_visible_in_agenda (bool — stage shows on the Agenda), is_fully_accessible (bool — track page fully public), is_cancel, mail_template_id. Live TCSW stages: Proposal(1)→Confirmed(2)→Announced(3, agenda✓)→Published(4, agenda✓ + fully-accessible✓)→Refused(5)/Cancelled(6). |
event.track.tag |
Agenda filter tag. |
name*, category_id (→event.track.tag.category), color, sequence, track_ids. Live TCSW: Founders/Innovation/Investors under category "Pillar". |
event.track.location |
A room / venue for tracks. |
name*, sequence. Live TCSW: Minneapolis, St. Paul. |
event.sponsor |
One sponsor / exhibitor on the wall (⚠️ website_event_exhibitor only). |
partner_id* (→res.partner, the org — logo/name come from here), sponsor_type_id* (→event.sponsor.type, the TIER), event_id*, exhibitor_type (sponsor|exhibitor|online), is_published (bool — on the wall), website_published/can_publish (computed), name (defaults from partner), subtitle, url (their website), email/phone, website_description (Html, the exhibitor blurb), website_image_url (computed logo url), show_on_ticket (bool — also list on the register/ticket page), hour_from/hour_to (online-exhibitor "open hours"), sequence, website_url (/event/<slug>/exhibitor/<slug>). |
event.sponsor.type |
A sponsor TIER — orders + ribbons the wall. |
name*, sequence (ranks tiers; lower = higher tier), display_ribbon_style (no_ribbon|Gold|Silver|Bronze). Live TCSW: Premier(Gold)→Presenter(Silver)→Supporting(Bronze)→Builder(no ribbon). |
event.registration |
One website RSVP. Read here; drive its lifecycle in [[everjust-events]]. |
event_id*, partner_id, name/email/phone, state (draft/open/done/cancel — open=confirmed), visitor_id (→website.visitor, the web session), utm_*, lead_ids (→crm.lead, via website_event_crm). |
event.event.ticket |
A free ticket type (segmentation, no price here). |
name, event_id, seats_max, seats_available/seats_used (computed), start_sale_datetime/end_sale_datetime (register window). |
The event minisite publish gate — read the codes, not the labels
An event's minisite is publicly reachable when:
is_published is True (website_published is the computed mirror the front-end reads), AND
website_visibility allows the visitor — public (anyone), link (only via direct URL, not
listed), or logged_users (must be signed in). Default is public.
Then which sub-pages/tabs exist is decided by the per-event menu booleans, each of which
creates/removes a website.event.menu:
website_menu = the master switch (the event has ANY of its own sub-menu). Off ⇒ no minisite
nav at all, just the bare event page.
register_menu → the Register / Practical page (the RSVP form + practical info).
website_track → the Agenda / Talks pages (needs website_event_track; else no-op/absent).
website_track_proposal → the Propose a talk submission form (public track proposals).
exhibitor_menu → the Exhibitors page / sponsor wall (needs website_event_exhibitor).
community_menu → the Community page; introduction_menu → the Home intro page.
So "build the event site" = publish the event + set website_visibility + flip the right menu
booleans. On live TCSW event 1 the website.event.menu rows are introduction (Home), register
(Practical), several track entries (Talks/Agenda), and track_proposal (Propose a talk) — the
exact set/count reflects which booleans are on, so read website.event.menu for the current tabs
rather than assuming a fixed list.
The AGENDA (track) publish gate — THREE parts
A track shows on the public Agenda/Talks only when all hold:
- The event's
website_track menu is on (the Agenda pages exist at all), AND
- The track's
stage_id is agenda-visible — stage_id.is_visible_in_agenda True (TCSW:
Announced or Published), and its own page is fully public only when
stage_id.is_fully_accessible (TCSW: Published), AND
- The track's
is_published is True (and can_publish means you have the right).
So publishing a session is move it to a public stage AND set is_published — not one flag. A
track in Proposal/Confirmed/Refused/Cancelled stays off the agenda even if is_published.
Drive the stage via stage_id (or the track's stage-action); the model recomputes agenda flags.
The SPONSOR wall
The Exhibitors page + the sponsor footer render event.sponsor records grouped and ranked by
sponsor_type_id.sequence, with the tier's display_ribbon_style ribbon, but only those with
is_published=True (and the event's exhibitor_menu on). Each sponsor is a partner_id at a tier;
the logo is the partner's image, the blurb is website_description, the outbound link is url.
exhibitor_type distinguishes a pure sponsor (logo/footer only) from an exhibitor/online
(gets its own exhibitor page). show_on_ticket also lists them on the register/ticket page.
Registration is the site's job only up to the register PAGE
Turning on register_menu exposes the website RSVP form; a submit creates an event.registration
(state open, auto-confirmed in this build) and — via website_event_crm if a rule exists — a
crm.lead. Everything past "the form exists" is [[everjust-events]]: seats, confirm/cancel,
lead rules, confirmation/reminder event.mail / event_sms. This skill reads registrations and
turns the page on; it does not run the lifecycle.
Recipes
Route each call through the Odoo MCP against the tenant DB (open the session per
[[everjust-agent-mcp]]; the event-heavy DB is tcstartupweek). Recipes use the MCP toolset
(search, get, create, update, call, describe_model, plus the WEBSITE TOOLS where noted);
ORM env[...] forms are shown where clearer.
0. First: confirm the add-ons exist on THIS tenant, and that you can publish
The single most important orientation. The agenda/exhibitor models exist on tcstartupweek only;
website edits need website-designer rights.
# Which event-website add-ons are installed here?
search('ir.module.module',
[['name','in',['website_event','website_event_track','website_event_exhibitor',
'website_event_crm','event_sale','website_event_sale']]],
['name','state'])
# Or just probe the model you need — KeyError where the add-on is absent:
describe_model('event.track') # -> error on connectdomain/headsup/weldon
describe_model('event.sponsor') # -> error there too
# Can YOU publish (vs only draft)? can_publish needs website-designer / admin:
get('event.event', [1], ['can_publish','is_published','website_published','website_visibility'])
If event.track/event.sponsor KeyError, this tenant has the register-only event site — you can
publish the event + register page but there is no agenda/sponsor wall; stop (installing the add-on is
platform-ops). If can_publish is False / your_access.write is False you lack website-designer
rights — don't retry with confirm:true (it's an ACL limit, not a confirm gate; see
[[everjust-agent-mcp]]).
1. Publish an event minisite and choose its sub-pages (the menu toggles)
"Building the event site" is records + toggles, NOT authoring a page. Set visibility, flip the menu
booleans, publish.
# Turn the event into a full minisite: publish + pick the tabs.
update('event.event', [1], {
'is_published': True, # <- puts it on /event and makes the minisite reachable
'website_visibility': 'public', # 'public' | 'link' (unlisted) | 'logged_users'
'website_menu': True, # MASTER: the event gets its own sub-menu at all
'introduction_menu': True, # -> "Home" tab
'register_menu': True, # -> "Register"/"Practical" page (the RSVP form)
'website_track': True, # -> "Agenda"/"Talks" pages (needs website_event_track)
'website_track_proposal': True, # -> "Propose a talk" form
'exhibitor_menu': True, # -> "Exhibitors" wall (needs website_event_exhibitor)
'community_menu': False,
'subtitle': 'Five days. Two cities. 200+ events.',
})
# Confirm what tabs now exist (auto-created website.event.menu rows):
search('website.event.menu', [['event_id','=',1]], ['menu_type','menu_id'])
get('event.event', [1], ['website_url','event_register_url','website_published','is_visible_on_website'])
website_menu=False ⇒ no minisite nav (just the bare event page). Turning website_track on where
website_event_track is not installed does nothing (no Agenda pages exist). The *_menu_ids are
read-only reflections — you flip the booleans, the framework builds/removes the website.event.menu
website.menu rows. To unpublish the whole minisite: update('event.event',[1],{'is_published': False}) (or set website_visibility:'link' to keep it reachable-by-URL but off the public list).
2. Add / publish an AGENDA track (session + speaker) — the 3-part gate
event.track only exists where website_event_track is installed (TCSW). A session shows on the
Agenda only when its stage is agenda-visible AND is_published AND the event's website_track is
on. So set the stage to a public one and publish.
# Find the public/agenda stages and rooms first:
search('event.track.stage', [], ['name','sequence','is_visible_in_agenda','is_fully_accessible'])
search('event.track.location', [], ['name']) # TCSW: Minneapolis / St. Paul
# Create a session with its speaker inline (speaker lives ON the track):
tid = create('event.track', {
'event_id': 1,
'name': 'Fundraising in a Down Market',
'date': '2026-09-15 15:00:00', # Datetime, UTC-naive; rendered in event.date_tz
'duration': 1.0, # hours (float)
'location_id': <minneapolis_loc_id>, # the room
'stage_id': <published_stage_id>, # <- MUST be agenda-visible (TCSW 'Announced'/'Published')
'tag_ids': [[6, 0, [<founders_tag_id>]]],
'description': '<p>A candid session on…</p>',
# speaker (inline partner_* fields):
'partner_name': 'Alex Rivera',
'partner_function': 'General Partner',
'partner_company_name': 'North Loop Ventures',
'partner_email': 'alex@example.com',
'partner_biography': '<p>Alex has backed 40+ seed-stage founders…</p>',
'is_published': True, # <- AND publish it
})
get('event.track', [tid], ['website_url','is_published','website_published','stage_id','can_publish'])
If it still doesn't show: check the event's website_track menu is on (recipe 1), the stage's
is_visible_in_agenda (a Proposal/Confirmed/Refused stage hides it), and is_published. To
publish an EXISTING proposed track, move its stage AND publish:
update('event.track',[tid],{'stage_id':<published_stage_id>,'is_published':True}). To pull a
session off the agenda without deleting: update('event.track',[tid],{'is_published':False}) (or move
it to a non-agenda stage). Use m2m tuples on tag_ids ([[6,0,ids]] replace, [[4,id]] add).
3. Add / publish a SPONSOR or exhibitor on the wall (by tier)
event.sponsor only exists where website_event_exhibitor is installed (TCSW). A sponsor is a
partner_id at a sponsor_type_id tier; the wall ranks by the tier's sequence + shows its ribbon,
but only is_published sponsors appear (and the event's exhibitor_menu must be on).
# Tiers already defined on TCSW: Premier(Gold) > Presenter(Silver) > Supporting(Bronze) > Builder(none)
search('event.sponsor.type', [], ['name','sequence','display_ribbon_style'])
# Resolve or create the org partner (logo/name come from the partner):
pid = search('res.partner', [['name','=ilike','JPMorgan Chase']], ['id'], 1) # or create one
sid = create('event.sponsor', {
'event_id': 1,
'partner_id': pid, # required — the org
'sponsor_type_id': <premier_type_id>, # required — the TIER (ribbon + rank)
'exhibitor_type': 'sponsor', # 'sponsor' (logo/footer) | 'exhibitor' | 'online'
'url': 'https://jpmorganchase.com',
'subtitle': 'Presenting sponsor',
'website_description': '<p>Proud to back the Twin Cities startup community.</p>',
'show_on_ticket': True, # also list on the register/ticket page
'is_published': True, # <- required to appear on the wall
})
get('event.sponsor', [sid], ['website_url','is_published','sponsor_type_id','website_image_url'])
The logo is the partner_id's image — set the partner's image_1920 if it has none. To add a NEW
tier: create('event.sponsor.type', {'name':'Community','sequence':5,'display_ribbon_style':'no_ribbon'}).
To pull a sponsor off the wall: update('event.sponsor',[sid],{'is_published':False}). A pure
exhibitor/online also gets its own /event/<slug>/exhibitor/<slug> page; a sponsor shows as a
logo in the wall/footer only.
4. Give the event page a cover, subtitle and SEO/OpenGraph
Presentation of the minisite header. Cover is the same JSON cover_properties shape as the blog
([[everjust-website-blog]]) — the background-image key inside a JSON blob.
# a) stage a cover image as a servable attachment:
att = create('ir.attachment', {
'name': 'tcsw-cover.jpg', 'datas': '<base64>',
'res_model': 'event.event', 'res_id': 1, 'mimetype': 'image/jpeg', 'public': True})
url = '/web/image/%s' % att
# b) write the cover JSON + subtitle + SEO:
import json
update('event.event', [1], {
'cover_properties': json.dumps({
'background-image': "url('%s')" % url,
'background_color_class': 'o_cc3', 'opacity': '0.2',
'resize_class': 'o_half_screen_height'}),
'subtitle': 'Five days. Two cities. One pass.',
'website_meta_title': 'Twin Cities Startup Week 2026',
'website_meta_description': '200+ events across Minneapolis & Saint Paul, Sept 14–18, 2026.',
'website_meta_og_img': url,
'seo_name': 'twin-cities-startup-week-2026', # URL slug override
})
background-image:"none" = no cover; keep the value valid JSON (a bare URL string breaks rendering).
event_register_url / website_url are computed — don't set them. Times on the page render in
date_tz (required on the event); the stored date_begin/date_end stay UTC-naive.
5. Read the registrations the site produced (hand off to the backend)
Turning on register_menu exposes the RSVP form; each submit is an event.registration. Read them
here — but drive their lifecycle / CRM / reminders in [[everjust-events]].
# Registrations for the event (state open = confirmed):
search('event.registration', [['event_id','=',1]],
['name','email','state','visitor_id','create_date','lead_ids'], 200, 'id desc')
# Seat/capacity snapshot for the register page:
get('event.event', [1], ['seats_limited','seats_max','seats_available','seats_taken'])
# Did website_event_crm turn RSVPs into leads? (needs an event.lead.rule — see [[everjust-events]])
search('crm.lead', [['id','in', <lead_ids>]], ['name','email_from','stage_id'])
To create/confirm/cancel a registration, wire lead rules, or schedule a confirmation/reminder
email/SMS, switch to [[everjust-events]] — those are backend mechanics, not site publishing. If
you need PAID tickets, note event_sale is uninstalled on every tenant (free RSVP only).
6. Diagnose "it's not showing on the site"
Walk the gates in order; each layer is a common cause.
# Event not on /event at all? -> publish flag + visibility + right site:
get('event.event', [1], ['is_published','website_published','website_visibility','website_id','website_url'])
# is_published False -> unpublished. website_visibility 'link'/'logged_users' -> not on public list.
# wrong website_id on a multi-site tenant -> shows on the other site only.
# Agenda/Exhibitors TAB missing? -> the menu toggle, and the add-on install:
get('event.event', [1], ['website_menu','website_track','exhibitor_menu','register_menu'])
search('website.event.menu', [['event_id','=',1]], ['menu_type']) # which tabs actually exist
describe_model('event.track') # KeyError => website_event_track not installed here
# A specific TRACK not on the agenda? -> the 3-part gate:
get('event.track', [<tid>], ['is_published','website_published','stage_id'])
get('event.track.stage', [<stage_id>], ['name','is_visible_in_agenda','is_fully_accessible'])
# stage not is_visible_in_agenda (Proposal/Confirmed/Refused/Cancelled) OR is_published False -> hidden.
# A SPONSOR not on the wall? -> is_published + the exhibitor menu:
get('event.sponsor', [<sid>], ['is_published','sponsor_type_id','exhibitor_type'])
And confirm you're on the right tenant DB — the agenda/sponsor wall only exists on
tcstartupweek (recipe 0). If the whole /event list is missing, website_event itself or the
everjust.public_website gate is the issue (see [[everjust-platform]]).
Pitfalls (everjust-specific)
The event minisite is GENERATED from records — do NOT author it with the WEBSITE TOOLS. The
website_event controller renders /event/<slug> and every sub-page (Home/Agenda/Talks/
Exhibitors/Register) from the event.event + its event.track/event.sponsor records + menu
toggles. Never website_new_page/website_edit_page/website_publish an event or track — you
publish with is_published on the record. The WEBSITE TOOLS are only for the STATIC /event list
chrome and marketing pages (Pitfall 2). Calling website_publish on an event does nothing to
expose it.
The TCSW /tickets and /sponsor MARKETING pages are hardcoded Tailwind QWeb, NOT
event.sponsor/ticket records. Module website_tcsw ports those pages pixel-faithfully with
Tailwind classes and static tier/sponsor cards that merely link to /event/<slug>/register.
Editing a tier card, adding a sponsor logo there, or changing copy is website_edit_page on that
ir.ui.view (the site-chrome path in [[everjust-platform]]) — it does not touch
event.sponsor. Conversely, publishing an event.sponsor updates the GENERATED Exhibitors wall,
NOT the hand-authored /sponsor page. Know which surface the task means before you act.
event.track (agenda/speakers) and event.sponsor (exhibitor wall) exist on ONLY SOME
tenants. website_event_track + website_event_exhibitor are installed on tcstartupweek
only; on connectdomain/headsup/weldon those models KeyError. Check install state (recipe
- before touching the agenda or sponsor wall — those tenants have the event minisite + register
page but no agenda/wall. Turning on
website_track/exhibitor_menu where the add-on is absent is
a no-op.
Menu toggles are the source of truth for which sub-pages exist — don't hand-create
website.event.menu. The booleans (website_menu master, register_menu, website_track,
website_track_proposal, exhibitor_menu, community_menu, introduction_menu) auto-create and
auto-remove the website.event.menu (+ website.menu) rows. Flip the boolean; the *_menu_ids
are read-only reflections. Hand-inserting menu rows desyncs from the toggle and breaks on the next
recompute.
Publishing a track is a THREE-part gate, not just is_published. A session appears on the
Agenda only when the event's website_track menu is on AND the track's stage_id is
agenda-visible (is_visible_in_agenda; its own page is fully public only at an
is_fully_accessible stage) AND is_published is True. A published track in a
Proposal/Confirmed/Refused/Cancelled stage still won't show. Move the stage AND set
is_published.
The event publish gate is is_published + website_visibility. is_published=True alone
isn't enough to be publicly listed if website_visibility is link (unlisted) or
logged_users (sign-in required). website_published/can_publish/is_visible_on_website are
computed — read them, don't write them. Write is_published and website_visibility.
Registration here is FREE RSVP — no paid checkout. event_sale/website_event_sale are
uninstalled on every tenant, so the website register form creates a free event.registration and
event.event.ticket is a free segmentation type only — no sale.order, no price, no payment page.
Don't promise a paid ticket flow or reason about invoicing on these tenants.
The registration LIFECYCLE, CRM leads, and reminder comms are the BACKEND — [[everjust-events]],
not here. This skill exposes the register page and reads the resulting event.registrations.
Confirming/cancelling, event.lead.rule (RSVP→crm.lead via website_event_crm), and
event.mail/event_sms confirmations & reminders all live in [[everjust-events]]. Don't run the
lifecycle from here; don't route event mail through [[everjust-mail-ops]] (it's stock
mail.template).
Sponsor logo + name come from the partner_id; the wall ranks/ribbons by
sponsor_type_id. A blank logo means the partner has no image_1920 — set it on the partner,
not the sponsor. Tier order is the type's sequence (lower = higher on the wall); the ribbon is
display_ribbon_style (Gold/Silver/Bronze/none). Only is_published sponsors render.
Everything is per-tenant DB, per-company_id, per-website_id. Events, tracks, sponsors, and
the register form are tenant-scoped; on a multi-site tenant an event with the wrong website_id
shows on the other site only. On any multi-tenant task confirm you are on the right DB (the
event-heavy one is tcstartupweek) and the correct website_id before publishing (see
[[everjust-platform]], [[everjust-agent-mcp]]).
See also
- [[everjust-events]] — the registration/CRM/reminder BACKEND (
event.registration lifecycle,
event.lead.rule, event.mail/event_sms, seats). This skill publishes the site + register
page; that skill runs what happens after someone RSVPs. Read it whenever the task leaves "expose
the event/agenda/sponsors" and enters "confirm/cancel/lead/reminder."
- [[everjust-platform]] — tenancy model,
/odoo debrand, the everjust.public_website gate, forced
light mode, per-tenant website/theme installs, and the QWeb site-chrome / marketing-page path
(WEBSITE TOOLS, Tailwind-utility QWeb under <t t-call="website.layout">, COW-into-a-site-view).
Read it to edit the /event list chrome or the TCSW /tickets / /sponsor marketing pages — those
are NOT the generated minisite.
- [[everjust-agent-mcp]] — how to connect to the tenant MCP and the generic
search/get/create/update/call/describe_model toolset + the WEBSITE TOOLS' signatures and
the confirm / ACL (website-designer) gates every recipe above relies on.
- [[everjust-website-blog]] — the sibling public-website CONTENT type (
blog.post). Same
cover_properties JSON cover + website_meta_* SEO, but a different publish gate
(is_published+post_date) and a follower-email side effect. Don't cross events with blog posts.
1---2name: everjust-website-events3description: EVERJUST Website Events — Agent Skill4---56# EVERJUST Website Events — Agent Skill78Build and publish the **public event site** of a live everjust.app tenant as an agent: turn an9`event.event` into a browsable minisite at `/event/<slug>` (with its Home / Practical / Register /10Agenda / Talks / Exhibitors / Propose-a-talk sub-pages), publish the **agenda** (`event.track`11sessions — speakers, times, rooms), publish the **sponsor / exhibitor** wall (`event.sponsor` by12tier), and drive **online registration** (`event.registration`) through the website register route —13all through the Odoo MCP / ORM (`search`, `get`, `create`, `update`, `call`, `describe_model`) plus14the MCP **WEBSITE TOOLS** where site-chrome is involved. See [[everjust-agent-mcp]] for opening the15session against the right tenant DB.1617The crucial everjust facts:1819- **This is STOCK Odoo 19** — `website_event` (the minisite + register route), `website_event_track`20 (`event.track` agenda + speakers + `track_proposal` form), `website_event_exhibitor`21 (`event.sponsor` = the sponsor/exhibitor wall), `website_event_crm` (registrations → `crm.lead`).22 There is **no custom everjust event addon.** Read fields from the live model (`describe_model` /23 `fields_get`), not from memory of an older Odoo.24- **The event minisite is GENERATED, not authored.** The `website_event` controller renders25 `/event/<slug>` and its sub-pages **from the event's records + per-event menu toggles** — you do26 NOT create a `website.page` per event or per track. The WEBSITE TOOLS27 (`website_new_page`/`website_edit_page`) are for the STATIC `/event` list chrome and marketing28 pages, **not** for the generated minisite (Pitfall 1). You "build the site" by writing records29 (`event.event` toggles, `event.track`, `event.sponsor`) and flipping `is_published`.30- **Per-event MENU TOGGLES decide which sub-pages exist.** Boolean fields on `event.event`31 (`website_menu` master, plus `register_menu`, `website_track`, `website_track_proposal`,32 `exhibitor_menu`, `community_menu`, `introduction_menu`) each auto-create/remove a33 `website.event.menu` row (a `website.menu` entry under the event) — that is how the "Agenda",34 "Talks", "Exhibitors", "Register", "Propose a talk" tabs appear. Flip the boolean; don't hand-make35 the menu rows (Pitfall 4).36- **`event.track` / `event.sponsor` exist on ONLY SOME tenants.** `website_event_track` and37 `website_event_exhibitor` are installed on **`tcstartupweek`** but **uninstalled on38 `connectdomain` / `headsup` / `weldon`** — there, `env["event.track"]` / `env["event.sponsor"]`39 raise `KeyError`. Check install state first (recipe 0). This mirrors [[everjust-events]]'s matrix.40- **Track visibility has a THREE-part gate, not one flag** (recipe 2 / Pitfall 5): the track's41 `stage_id` must be a public/agenda stage, AND `is_published` True, AND the event's `website_track`42 menu on. Publishing a track is NOT just `is_published`.43- **The TCSW MARKETING pages are a SEPARATE hand-authored Tailwind QWeb layer.** `/tickets` and44 `/sponsor` on tcstartupweek are pixel-faithful Tailwind `website.page` QWeb (module `website_tcsw`)45 with **hardcoded** tier cards that merely link to `/event/<slug>/register` — they are NOT rendered46 from `event.sponsor` / ticket records. Editing them is the site-chrome path47 ([[everjust-platform]] / `website_edit_page`), NOT this skill; this skill is the generated event48 minisite + its content records (Pitfall 2).49- **Registration on these tenants is FREE RSVP.** `event_sale` / `website_event_sale` are50 uninstalled everywhere — the website register form creates a free `event.registration`; there is51 no paid checkout / `sale.order`. Ticket *types* (`event.event.ticket`) are free segmentation only.5253You reach the ORM + WEBSITE TOOLS through the platform's Odoo MCP — see [[everjust-agent-mcp]] for54opening the session against the right tenant DB and the tool signatures / `confirm` + ACL gates.55Every recipe below is expressed through that toolset. **Website edits (publishing, menu toggles,56editing chrome) require the connected user to have website-designer / admin rights** — check57`can_publish` first (recipe 0).5859## When to use this skill6061- **Publish / unpublish an event minisite** — flip an `event.event`'s `is_published` (+ set62 `website_visibility`) so it appears at `/event`, and choose which sub-pages exist via the menu63 toggles (`website_menu`, `register_menu`, `website_track`, `exhibitor_menu`, …).64- **Build / publish the agenda** — create or publish `event.track` sessions (name, `date`,65 `duration`, `location_id`/room, `stage_id`, speaker `partner_*` fields, `tag_ids`), so the66 Agenda/Talks pages render them.67- **Manage the sponsor / exhibitor wall** — create or publish `event.sponsor` records (a `partner_id`68 at a `sponsor_type_id` tier, `exhibitor_type` sponsor/exhibitor/online, logo, `url`,69 `website_description`), so the Exhibitors page and sponsor footer render them by tier.70- **Enable / read online registration** — turn on the Register page, point people at the website71 register route, and read the `event.registration`s it produced (registration mechanics, CRM leads,72 confirmation/reminder comms are the BACKEND — [[everjust-events]]).73- **Tune an event page's presentation** — cover image (`cover_properties`), `subtitle`,74 SEO/OpenGraph (`website_meta_*`, `seo_name`), `website_visibility` (public / link-only / logged-in).75- **Diagnose** — an event not on `/event` (unpublished / visibility / wrong `website_id`), an Agenda76 or Exhibitors tab missing (menu toggle off, or the add-on not installed), a track not showing77 (stage/`is_published`), a sponsor not on the wall (`is_published`).7879**Do NOT use this skill for**, and stop if the task is really:8081- **The registration/CRM/reminder BACKEND.** Creating/confirming/cancelling an `event.registration`82 by hand, wiring `event.lead.rule` (registrations → `crm.lead`), scheduling `event.mail` /83 `event_sms` confirmations & reminders, seats logic — all that is [[everjust-events]]. THIS skill is84 the public *site*: exposing the event, agenda, sponsors, and the register *page*; it reads85 registrations but does not run the registration lifecycle.86- **The static `/event` LIST chrome or a MARKETING page.** The `/event` index styling and the TCSW87 `/tickets` / `/sponsor` Tailwind pages are `website.page` / `ir.ui.view` QWeb — edit them with the88 WEBSITE TOOLS keeping the existing Tailwind idiom; that's the site-chrome path in89 [[everjust-platform]], NOT the generated minisite. Don't confuse hardcoded marketing tier cards90 with `event.sponsor` records.91- **Blog posts** — `blog.post` / `website_blog`; see [[everjust-website-blog]]. An event is not a92 blog post (different publish gate).93- **Installing `website_event_track` / `website_event_exhibitor` on a tenant that lacks them** — that94 is platform-ops (module install), not this skill. On a tenant without the add-on the agenda /95 exhibitor models simply don't exist; stop.96- **Booths / paid tickets / live streaming / meet** — `event_booth*`, `event_sale` /97 `website_event_sale`, `website_event_track_live`, `website_event_meet` are **uninstalled on every98 everjust tenant** at last check. There is no booth booking, paid checkout, or live-stream page.99100## Where it's installed (verified live, 2026-07)101102One tenant DB per customer; the event-website add-ons install with the tenant's public-website theme.103Verified live via `ir_module_module.state` (and consistent with [[everjust-events]]):104105| Tenant DB | website_event (minisite + register) | website_event_track (`event.track`) | website_event_exhibitor (`event.sponsor`) | website_event_crm |106|---|---|---|---|---|107| `tcstartupweek` | ✅ | **✅** | **✅** | ✅ |108| `connectdomain` | ✅ | **uninstalled** | **uninstalled** | ✅ |109| `headsup` | ✅ | uninstalled | uninstalled | (event) |110| `weldon` | ✅ | uninstalled | uninstalled | (event) |111112`event_sale`, `website_event_sale`, `event_booth*`, `website_event_booth*`,113`website_event_track_live`, `website_event_meet` were **uninstalled on all** at last check. So the114**agenda + sponsor wall only exist on `tcstartupweek`**; the other tenants get the event minisite +115register page only. **Always confirm on your target DB (recipe 0)** — `env["event.track"]` /116`env["event.sponsor"]` `KeyError` where the add-on isn't installed. On `tcstartupweek` at last check:117**7 events** (2015-2026), and only the **2026 event (id 1)** is published (`is_published=True`,118`website_visibility='public'`) — it carries ~12 published `event.track` sessions and its `track` +119`register` + `track_proposal` menus on, against **~1066 total tracks + ~52 sponsors** (most on the120older, unpublished archive events, and most sponsors not yet published on the 2026 event). Treat121these counts as volatile — read them live, don't hard-code them.122123## Architecture (the model map)124125Everything is per-tenant DB, per-`company_id`, and per-`website_id`. Real fields below are from126`fields_get` on the live `tcstartupweek` DB (Odoo 19). Read fields live — don't assume older shapes.127128| Model | Role | Key fields (real, confirmed live) |129|---|---|---|130| `event.event` | **The event = one minisite.** Its booleans decide which sub-pages exist; `is_published` puts it on `/event`. | `name`*, `subtitle`, `date_begin`*/`date_end`* (**Datetime, UTC-naive**), `date_tz`* (display tz — required), `seats_limited`*/`seats_max`/`seats_available`/`seats_taken` (computed), `organizer_id`/`address_id` (→`res.partner`), `stage_id` (→`event.stage`), **`is_published`** (bool — on `/event` list), `website_published` (**computed mirror — read, don't write**), `can_publish` (**computed — may YOU publish**), `is_visible_on_website` (computed), **`website_visibility`*** (`public`\|`link`\|`logged_users`), `website_id` (→`website`; `False`=all sites), `website_url` (`/event/<slug>`), `event_register_url` (computed register link), `menu_id` (→`website.menu`, the event's root menu), **menu toggles** → `website_menu` (master), `register_menu`, `community_menu`, `introduction_menu`, `website_track`, `website_track_proposal`, `exhibitor_menu` (booleans; each spawns a `website.event.menu`), the matching `*_menu_ids` (→`website.event.menu`, read-only), `cover_properties` (**JSON text cover, same shape as blog**), `ticket_instructions` (Html on register page), `question_ids`/`general_question_ids`/`specific_question_ids` (→`event.question`, register-form questions), `event_ticket_ids` (→`event.event.ticket`, free ticket types), `track_ids`/`track_count` (→`event.track`), `sponsor_ids`/`sponsor_count` (→`event.sponsor`), `tag_ids` (→`event.tag`), `allowed_track_tag_ids`/`tracks_tag_ids` (agenda tag filter), `seo_name`, `website_meta_title`/`website_meta_description`/`website_meta_keywords`/`website_meta_og_img` |131| `website.event.menu` | **One sub-page/tab of the minisite**, auto-created by a menu toggle. Don't create by hand. | `event_id`, `menu_id` (→`website.menu`), **`menu_type`*** (`community`\|`introduction`\|`register`\|`other`\|`exhibitor`\|`track`\|`track_proposal`), `view_id` (→`ir.ui.view`), `seo_name`, `website_meta_*`. (Labels are relabeled in the UI: `introduction`="Home", `register`="Practical", `track`="Talks"/"Agenda".) |132| `event.track` | **One agenda session / talk** (⚠️ `website_event_track` only). Has the speaker inline. | `name`*, `event_id`*, **`stage_id`*** (→`event.track.stage` — **the publish/agenda gate**), **`is_published`** (bool), `website_published` (computed), `can_publish` (computed), `date` (Datetime, session start), `date_end`/`duration` (float hours), `location_id` (→`event.track.location`, the room/venue), `tag_ids` (→`event.track.tag`), `color`, `priority`, `description` (Html), **speaker inline:** `partner_id` (→`res.partner`), `partner_name`, `partner_email`, `partner_phone`, `partner_function` (job title), `partner_company_name`, `partner_biography` (Html), `contact_email`/`contact_phone`, `website_cta`/`website_cta_title`/`website_cta_url` (a call-to-action button on the track page), `wishlisted_by_default`, `website_url` (`/event/<slug>/track/<track-slug>`), `seo_name`, `website_meta_*`. (TCSW adds a studio field `x_venue_partner_id`.) |133| `event.track.stage` | **Kanban stage that ALSO gates public agenda visibility.** | `name`*, `sequence`, `fold`, **`is_visible_in_agenda`** (bool — stage shows on the Agenda), **`is_fully_accessible`** (bool — track page fully public), `is_cancel`, `mail_template_id`. Live TCSW stages: `Proposal`(1)→`Confirmed`(2)→`Announced`(3, agenda✓)→`Published`(4, agenda✓ + fully-accessible✓)→`Refused`(5)/`Cancelled`(6). |134| `event.track.tag` | Agenda filter tag. | `name`*, `category_id` (→`event.track.tag.category`), `color`, `sequence`, `track_ids`. Live TCSW: `Founders`/`Innovation`/`Investors` under category "Pillar". |135| `event.track.location` | A room / venue for tracks. | `name`*, `sequence`. Live TCSW: `Minneapolis`, `St. Paul`. |136| `event.sponsor` | **One sponsor / exhibitor on the wall** (⚠️ `website_event_exhibitor` only). | `partner_id`* (→`res.partner`, the org — logo/name come from here), **`sponsor_type_id`*** (→`event.sponsor.type`, the TIER), `event_id`*, **`exhibitor_type`** (`sponsor`\|`exhibitor`\|`online`), **`is_published`** (bool — on the wall), `website_published`/`can_publish` (computed), `name` (defaults from partner), `subtitle`, `url` (their website), `email`/`phone`, `website_description` (Html, the exhibitor blurb), `website_image_url` (computed logo url), `show_on_ticket` (bool — also list on the register/ticket page), `hour_from`/`hour_to` (online-exhibitor "open hours"), `sequence`, `website_url` (`/event/<slug>/exhibitor/<slug>`). |137| `event.sponsor.type` | **A sponsor TIER** — orders + ribbons the wall. | `name`*, `sequence` (ranks tiers; lower = higher tier), **`display_ribbon_style`** (`no_ribbon`\|`Gold`\|`Silver`\|`Bronze`). Live TCSW: `Premier`(Gold)→`Presenter`(Silver)→`Supporting`(Bronze)→`Builder`(no ribbon). |138| `event.registration` | **One website RSVP.** Read here; drive its lifecycle in [[everjust-events]]. | `event_id`*, `partner_id`, `name`/`email`/`phone`, `state` (`draft`/`open`/`done`/`cancel` — **`open`=confirmed**), `visitor_id` (→`website.visitor`, the web session), `utm_*`, `lead_ids` (→`crm.lead`, via `website_event_crm`). |139| `event.event.ticket` | A **free** ticket *type* (segmentation, no price here). | `name`*, `event_id`*, `seats_max`, `seats_available`/`seats_used` (computed), `start_sale_datetime`/`end_sale_datetime` (register window). |140141### The event minisite publish gate — read the codes, not the labels142143An event's **minisite is publicly reachable** when:1441. **`is_published` is True** (`website_published` is the computed mirror the front-end reads), AND1452. **`website_visibility` allows the visitor** — `public` (anyone), `link` (only via direct URL, not146 listed), or `logged_users` (must be signed in). Default is `public`.147148Then **which sub-pages/tabs exist** is decided by the per-event menu booleans, each of which149creates/removes a `website.event.menu`:150- `website_menu` = the **master** switch (the event has ANY of its own sub-menu). Off ⇒ no minisite151 nav at all, just the bare event page.152- `register_menu` → the **Register / Practical** page (the RSVP form + practical info).153- `website_track` → the **Agenda / Talks** pages (needs `website_event_track`; else no-op/absent).154- `website_track_proposal` → the **Propose a talk** submission form (public track proposals).155- `exhibitor_menu` → the **Exhibitors** page / sponsor wall (needs `website_event_exhibitor`).156- `community_menu` → the **Community** page; `introduction_menu` → the **Home** intro page.157158So "build the event site" = publish the event + set `website_visibility` + flip the right menu159booleans. On live TCSW event 1 the `website.event.menu` rows are `introduction` (Home), `register`160(Practical), several `track` entries (Talks/Agenda), and `track_proposal` (Propose a talk) — the161exact set/count reflects which booleans are on, so read `website.event.menu` for the current tabs162rather than assuming a fixed list.163164### The AGENDA (track) publish gate — THREE parts165166A track shows on the public Agenda/Talks **only when all hold**:1671. The event's **`website_track` menu is on** (the Agenda pages exist at all), AND1682. The track's **`stage_id` is agenda-visible** — `stage_id.is_visible_in_agenda` True (TCSW:169 `Announced` or `Published`), and its own page is fully public only when170 `stage_id.is_fully_accessible` (TCSW: `Published`), AND1713. The track's **`is_published` is True** (and `can_publish` means you have the right).172173So publishing a session is **move it to a public stage AND set `is_published`** — not one flag. A174track in `Proposal`/`Confirmed`/`Refused`/`Cancelled` stays off the agenda even if `is_published`.175Drive the stage via `stage_id` (or the track's stage-action); the model recomputes agenda flags.176177### The SPONSOR wall178179The Exhibitors page + the sponsor footer render `event.sponsor` records **grouped and ranked by180`sponsor_type_id.sequence`**, with the tier's `display_ribbon_style` ribbon, but **only those with181`is_published=True`** (and the event's `exhibitor_menu` on). Each sponsor is a `partner_id` at a tier;182the logo is the partner's image, the blurb is `website_description`, the outbound link is `url`.183`exhibitor_type` distinguishes a pure `sponsor` (logo/footer only) from an `exhibitor`/`online`184(gets its own exhibitor page). `show_on_ticket` also lists them on the register/ticket page.185186### Registration is the site's job only up to the register PAGE187188Turning on `register_menu` exposes the website RSVP form; a submit creates an `event.registration`189(state `open`, auto-confirmed in this build) and — via `website_event_crm` if a rule exists — a190`crm.lead`. **Everything past "the form exists" is [[everjust-events]]:** seats, confirm/cancel,191lead rules, confirmation/reminder `event.mail` / `event_sms`. This skill reads registrations and192turns the page on; it does not run the lifecycle.193194## Recipes195196Route each call through the Odoo MCP against the tenant DB (open the session per197[[everjust-agent-mcp]]; the event-heavy DB is `tcstartupweek`). Recipes use the MCP toolset198(`search`, `get`, `create`, `update`, `call`, `describe_model`, plus the WEBSITE TOOLS where noted);199ORM `env[...]` forms are shown where clearer.200201### 0. First: confirm the add-ons exist on THIS tenant, and that you can publish202203The single most important orientation. The agenda/exhibitor models exist on `tcstartupweek` only;204website edits need website-designer rights.205206```207# Which event-website add-ons are installed here?208search('ir.module.module',209 [['name','in',['website_event','website_event_track','website_event_exhibitor',210 'website_event_crm','event_sale','website_event_sale']]],211 ['name','state'])212# Or just probe the model you need — KeyError where the add-on is absent:213describe_model('event.track') # -> error on connectdomain/headsup/weldon214describe_model('event.sponsor') # -> error there too215# Can YOU publish (vs only draft)? can_publish needs website-designer / admin:216get('event.event', [1], ['can_publish','is_published','website_published','website_visibility'])217```218If `event.track`/`event.sponsor` `KeyError`, this tenant has the register-only event site — you can219publish the event + register page but there is no agenda/sponsor wall; stop (installing the add-on is220platform-ops). If `can_publish` is False / `your_access.write` is False you lack website-designer221rights — **don't retry with `confirm:true`** (it's an ACL limit, not a confirm gate; see222[[everjust-agent-mcp]]).223224### 1. Publish an event minisite and choose its sub-pages (the menu toggles)225226"Building the event site" is records + toggles, NOT authoring a page. Set visibility, flip the menu227booleans, publish.228229```230# Turn the event into a full minisite: publish + pick the tabs.231update('event.event', [1], {232 'is_published': True, # <- puts it on /event and makes the minisite reachable233 'website_visibility': 'public', # 'public' | 'link' (unlisted) | 'logged_users'234 'website_menu': True, # MASTER: the event gets its own sub-menu at all235 'introduction_menu': True, # -> "Home" tab236 'register_menu': True, # -> "Register"/"Practical" page (the RSVP form)237 'website_track': True, # -> "Agenda"/"Talks" pages (needs website_event_track)238 'website_track_proposal': True, # -> "Propose a talk" form239 'exhibitor_menu': True, # -> "Exhibitors" wall (needs website_event_exhibitor)240 'community_menu': False,241 'subtitle': 'Five days. Two cities. 200+ events.',242})243# Confirm what tabs now exist (auto-created website.event.menu rows):244search('website.event.menu', [['event_id','=',1]], ['menu_type','menu_id'])245get('event.event', [1], ['website_url','event_register_url','website_published','is_visible_on_website'])246```247`website_menu=False` ⇒ no minisite nav (just the bare event page). Turning `website_track` on where248`website_event_track` is **not** installed does nothing (no Agenda pages exist). The `*_menu_ids` are249read-only reflections — you flip the booleans, the framework builds/removes the `website.event.menu`250+ `website.menu` rows. To **unpublish** the whole minisite: `update('event.event',[1],{'is_published':251False})` (or set `website_visibility:'link'` to keep it reachable-by-URL but off the public list).252253### 2. Add / publish an AGENDA track (session + speaker) — the 3-part gate254255`event.track` only exists where `website_event_track` is installed (TCSW). A session shows on the256Agenda only when its **stage is agenda-visible AND `is_published` AND the event's `website_track` is257on**. So set the stage to a public one *and* publish.258259```260# Find the public/agenda stages and rooms first:261search('event.track.stage', [], ['name','sequence','is_visible_in_agenda','is_fully_accessible'])262search('event.track.location', [], ['name']) # TCSW: Minneapolis / St. Paul263264# Create a session with its speaker inline (speaker lives ON the track):265tid = create('event.track', {266 'event_id': 1,267 'name': 'Fundraising in a Down Market',268 'date': '2026-09-15 15:00:00', # Datetime, UTC-naive; rendered in event.date_tz269 'duration': 1.0, # hours (float)270 'location_id': <minneapolis_loc_id>, # the room271 'stage_id': <published_stage_id>, # <- MUST be agenda-visible (TCSW 'Announced'/'Published')272 'tag_ids': [[6, 0, [<founders_tag_id>]]],273 'description': '<p>A candid session on…</p>',274 # speaker (inline partner_* fields):275 'partner_name': 'Alex Rivera',276 'partner_function': 'General Partner',277 'partner_company_name': 'North Loop Ventures',278 'partner_email': 'alex@example.com',279 'partner_biography': '<p>Alex has backed 40+ seed-stage founders…</p>',280 'is_published': True, # <- AND publish it281})282get('event.track', [tid], ['website_url','is_published','website_published','stage_id','can_publish'])283```284If it still doesn't show: check the event's `website_track` menu is on (recipe 1), the stage's285`is_visible_in_agenda` (a `Proposal`/`Confirmed`/`Refused` stage hides it), and `is_published`. To286publish an EXISTING proposed track, move its stage AND publish:287`update('event.track',[tid],{'stage_id':<published_stage_id>,'is_published':True})`. To pull a288session off the agenda without deleting: `update('event.track',[tid],{'is_published':False})` (or move289it to a non-agenda stage). Use m2m tuples on `tag_ids` (`[[6,0,ids]]` replace, `[[4,id]]` add).290291### 3. Add / publish a SPONSOR or exhibitor on the wall (by tier)292293`event.sponsor` only exists where `website_event_exhibitor` is installed (TCSW). A sponsor is a294`partner_id` at a `sponsor_type_id` tier; the wall ranks by the tier's `sequence` + shows its ribbon,295but only `is_published` sponsors appear (and the event's `exhibitor_menu` must be on).296297```298# Tiers already defined on TCSW: Premier(Gold) > Presenter(Silver) > Supporting(Bronze) > Builder(none)299search('event.sponsor.type', [], ['name','sequence','display_ribbon_style'])300# Resolve or create the org partner (logo/name come from the partner):301pid = search('res.partner', [['name','=ilike','JPMorgan Chase']], ['id'], 1) # or create one302303sid = create('event.sponsor', {304 'event_id': 1,305 'partner_id': pid, # required — the org306 'sponsor_type_id': <premier_type_id>, # required — the TIER (ribbon + rank)307 'exhibitor_type': 'sponsor', # 'sponsor' (logo/footer) | 'exhibitor' | 'online'308 'url': 'https://jpmorganchase.com',309 'subtitle': 'Presenting sponsor',310 'website_description': '<p>Proud to back the Twin Cities startup community.</p>',311 'show_on_ticket': True, # also list on the register/ticket page312 'is_published': True, # <- required to appear on the wall313})314get('event.sponsor', [sid], ['website_url','is_published','sponsor_type_id','website_image_url'])315```316The **logo** is the `partner_id`'s image — set the partner's `image_1920` if it has none. To add a NEW317tier: `create('event.sponsor.type', {'name':'Community','sequence':5,'display_ribbon_style':'no_ribbon'})`.318To pull a sponsor off the wall: `update('event.sponsor',[sid],{'is_published':False})`. A pure319`exhibitor`/`online` also gets its own `/event/<slug>/exhibitor/<slug>` page; a `sponsor` shows as a320logo in the wall/footer only.321322### 4. Give the event page a cover, subtitle and SEO/OpenGraph323324Presentation of the minisite header. Cover is the same JSON `cover_properties` shape as the blog325([[everjust-website-blog]]) — the `background-image` key inside a JSON blob.326327```328# a) stage a cover image as a servable attachment:329att = create('ir.attachment', {330 'name': 'tcsw-cover.jpg', 'datas': '<base64>',331 'res_model': 'event.event', 'res_id': 1, 'mimetype': 'image/jpeg', 'public': True})332url = '/web/image/%s' % att333334# b) write the cover JSON + subtitle + SEO:335import json336update('event.event', [1], {337 'cover_properties': json.dumps({338 'background-image': "url('%s')" % url,339 'background_color_class': 'o_cc3', 'opacity': '0.2',340 'resize_class': 'o_half_screen_height'}),341 'subtitle': 'Five days. Two cities. One pass.',342 'website_meta_title': 'Twin Cities Startup Week 2026',343 'website_meta_description': '200+ events across Minneapolis & Saint Paul, Sept 14–18, 2026.',344 'website_meta_og_img': url,345 'seo_name': 'twin-cities-startup-week-2026', # URL slug override346})347```348`background-image:"none"` = no cover; keep the value valid JSON (a bare URL string breaks rendering).349`event_register_url` / `website_url` are computed — don't set them. Times on the page render in350`date_tz` (required on the event); the stored `date_begin`/`date_end` stay UTC-naive.351352### 5. Read the registrations the site produced (hand off to the backend)353354Turning on `register_menu` exposes the RSVP form; each submit is an `event.registration`. Read them355here — but drive their lifecycle / CRM / reminders in [[everjust-events]].356357```358# Registrations for the event (state open = confirmed):359search('event.registration', [['event_id','=',1]],360 ['name','email','state','visitor_id','create_date','lead_ids'], 200, 'id desc')361# Seat/capacity snapshot for the register page:362get('event.event', [1], ['seats_limited','seats_max','seats_available','seats_taken'])363# Did website_event_crm turn RSVPs into leads? (needs an event.lead.rule — see [[everjust-events]])364search('crm.lead', [['id','in', <lead_ids>]], ['name','email_from','stage_id'])365```366To create/confirm/cancel a registration, wire lead rules, or schedule a confirmation/reminder367email/SMS, **switch to [[everjust-events]]** — those are backend mechanics, not site publishing. If368you need PAID tickets, note `event_sale` is uninstalled on every tenant (free RSVP only).369370### 6. Diagnose "it's not showing on the site"371372Walk the gates in order; each layer is a common cause.373374```375# Event not on /event at all? -> publish flag + visibility + right site:376get('event.event', [1], ['is_published','website_published','website_visibility','website_id','website_url'])377# is_published False -> unpublished. website_visibility 'link'/'logged_users' -> not on public list.378# wrong website_id on a multi-site tenant -> shows on the other site only.379380# Agenda/Exhibitors TAB missing? -> the menu toggle, and the add-on install:381get('event.event', [1], ['website_menu','website_track','exhibitor_menu','register_menu'])382search('website.event.menu', [['event_id','=',1]], ['menu_type']) # which tabs actually exist383describe_model('event.track') # KeyError => website_event_track not installed here384385# A specific TRACK not on the agenda? -> the 3-part gate:386get('event.track', [<tid>], ['is_published','website_published','stage_id'])387get('event.track.stage', [<stage_id>], ['name','is_visible_in_agenda','is_fully_accessible'])388# stage not is_visible_in_agenda (Proposal/Confirmed/Refused/Cancelled) OR is_published False -> hidden.389390# A SPONSOR not on the wall? -> is_published + the exhibitor menu:391get('event.sponsor', [<sid>], ['is_published','sponsor_type_id','exhibitor_type'])392```393And confirm you're on the **right tenant DB** — the agenda/sponsor wall only exists on394`tcstartupweek` (recipe 0). If the whole `/event` list is missing, `website_event` itself or the395`everjust.public_website` gate is the issue (see [[everjust-platform]]).396397## Pitfalls (everjust-specific)3983991. **The event minisite is GENERATED from records — do NOT author it with the WEBSITE TOOLS.** The400 `website_event` controller renders `/event/<slug>` and every sub-page (Home/Agenda/Talks/401 Exhibitors/Register) from the `event.event` + its `event.track`/`event.sponsor` records + menu402 toggles. **Never** `website_new_page`/`website_edit_page`/`website_publish` an event or track — you403 publish with `is_published` on the record. The WEBSITE TOOLS are only for the STATIC `/event` list404 chrome and marketing pages (Pitfall 2). Calling `website_publish` on an event does nothing to405 expose it.4064072. **The TCSW `/tickets` and `/sponsor` MARKETING pages are hardcoded Tailwind QWeb, NOT408 `event.sponsor`/ticket records.** Module `website_tcsw` ports those pages pixel-faithfully with409 Tailwind classes and **static** tier/sponsor cards that merely link to `/event/<slug>/register`.410 Editing a tier card, adding a sponsor logo there, or changing copy is `website_edit_page` on that411 `ir.ui.view` (the site-chrome path in [[everjust-platform]]) — it does **not** touch412 `event.sponsor`. Conversely, publishing an `event.sponsor` updates the GENERATED Exhibitors wall,413 NOT the hand-authored `/sponsor` page. Know which surface the task means before you act.4144153. **`event.track` (agenda/speakers) and `event.sponsor` (exhibitor wall) exist on ONLY SOME416 tenants.** `website_event_track` + `website_event_exhibitor` are installed on **`tcstartupweek`417 only**; on `connectdomain`/`headsup`/`weldon` those models `KeyError`. Check install state (recipe418 0) before touching the agenda or sponsor wall — those tenants have the event minisite + register419 page but no agenda/wall. Turning on `website_track`/`exhibitor_menu` where the add-on is absent is420 a no-op.4214224. **Menu toggles are the source of truth for which sub-pages exist — don't hand-create423 `website.event.menu`.** The booleans (`website_menu` master, `register_menu`, `website_track`,424 `website_track_proposal`, `exhibitor_menu`, `community_menu`, `introduction_menu`) auto-create and425 auto-remove the `website.event.menu` (+ `website.menu`) rows. Flip the boolean; the `*_menu_ids`426 are read-only reflections. Hand-inserting menu rows desyncs from the toggle and breaks on the next427 recompute.4284295. **Publishing a track is a THREE-part gate, not just `is_published`.** A session appears on the430 Agenda only when the event's **`website_track` menu is on** AND the track's **`stage_id` is431 agenda-visible** (`is_visible_in_agenda`; its own page is fully public only at an432 `is_fully_accessible` stage) AND **`is_published`** is True. A published track in a433 `Proposal`/`Confirmed`/`Refused`/`Cancelled` stage still won't show. Move the stage AND set434 `is_published`.4354366. **The event publish gate is `is_published` + `website_visibility`.** `is_published=True` alone437 isn't enough to be publicly *listed* if `website_visibility` is `link` (unlisted) or438 `logged_users` (sign-in required). `website_published`/`can_publish`/`is_visible_on_website` are439 **computed — read them, don't write them.** Write `is_published` and `website_visibility`.4404417. **Registration here is FREE RSVP — no paid checkout.** `event_sale`/`website_event_sale` are442 uninstalled on every tenant, so the website register form creates a free `event.registration` and443 `event.event.ticket` is a free segmentation type only — no `sale.order`, no price, no payment page.444 Don't promise a paid ticket flow or reason about invoicing on these tenants.4454468. **The registration LIFECYCLE, CRM leads, and reminder comms are the BACKEND — [[everjust-events]],447 not here.** This skill exposes the register *page* and reads the resulting `event.registration`s.448 Confirming/cancelling, `event.lead.rule` (RSVP→`crm.lead` via `website_event_crm`), and449 `event.mail`/`event_sms` confirmations & reminders all live in [[everjust-events]]. Don't run the450 lifecycle from here; don't route event mail through [[everjust-mail-ops]] (it's stock451 `mail.template`).4524539. **Sponsor logo + name come from the `partner_id`; the wall ranks/ribbons by454 `sponsor_type_id`.** A blank logo means the partner has no `image_1920` — set it on the partner,455 not the sponsor. Tier order is the type's `sequence` (lower = higher on the wall); the ribbon is456 `display_ribbon_style` (Gold/Silver/Bronze/none). Only `is_published` sponsors render.45745810. **Everything is per-tenant DB, per-`company_id`, per-`website_id`.** Events, tracks, sponsors, and459 the register form are tenant-scoped; on a multi-site tenant an event with the wrong `website_id`460 shows on the other site only. On any multi-tenant task confirm you are on the right DB (the461 event-heavy one is `tcstartupweek`) and the correct `website_id` before publishing (see462 [[everjust-platform]], [[everjust-agent-mcp]]).463464## See also465466- [[everjust-events]] — the **registration/CRM/reminder BACKEND** (`event.registration` lifecycle,467 `event.lead.rule`, `event.mail`/`event_sms`, seats). This skill publishes the *site* + register468 *page*; that skill runs what happens after someone RSVPs. Read it whenever the task leaves "expose469 the event/agenda/sponsors" and enters "confirm/cancel/lead/reminder."470- [[everjust-platform]] — tenancy model, `/odoo` debrand, the `everjust.public_website` gate, forced471 light mode, per-tenant website/theme installs, and the QWeb **site-chrome / marketing-page path**472 (WEBSITE TOOLS, Tailwind-utility QWeb under `<t t-call="website.layout">`, COW-into-a-site-view).473 Read it to edit the `/event` list chrome or the TCSW `/tickets` / `/sponsor` marketing pages — those474 are NOT the generated minisite.475- [[everjust-agent-mcp]] — how to connect to the tenant MCP and the generic476 `search`/`get`/`create`/`update`/`call`/`describe_model` toolset + the WEBSITE TOOLS' signatures and477 the `confirm` / ACL (website-designer) gates every recipe above relies on.478- [[everjust-website-blog]] — the sibling public-website CONTENT type (`blog.post`). Same479 `cover_properties` JSON cover + `website_meta_*` SEO, but a different publish gate480 (`is_published`+`post_date`) and a follower-email side effect. Don't cross events with blog posts.