Admin guide
How to run a client's site from the admin panel, in the order the work happens. For the narrative "why does this work this way" version, hand a new staff member docs/handbook/for-event-staff.md first; come back here for a specific task. For what the look is made of underneath — every site style, every option, every token — see design-reference.md. You do not need it to run a site.
Open Session pitches to review private proposals. Share /pitch for public intake. Operators set the call deadline, or choose Open until I close it for a call with no deadline, and choose this site's form or an external form, such as Airtable. With an Airtable form, an operator connects the approved table once; staff then choose Refresh from Airtable, or an operator turns on an automatic refresh. A refresh adds new pitches, updates changed text, keeps every decision and note, and marks changed or removed records in the queue. A person who pitches through this site's form gets a receipt. Receipts are on by default; to turn them off, choose Staff send each receipt under Manage pitch emails. Staff send acceptance and decline emails from each pitch, after a preview. Saving a decision sends no email. See Operate session pitch intake for decisions, pitch emails, the Airtable refresh, CSV import and export, draft conversion, and private data boundaries.
Draft and live are separate almost everywhere below. Saving writes a draft; publishing copies it to what attendees see. If a change is not showing up on the public site, check that you published, not only saved.
The banner above each admin page counts what is saved and not published. It names the count by kind, such as "2 unpublished changes: 1 content block, 1 page.", and links to Unpublished changes, where you publish them. It shows on every admin page except that one, and only while the count is above zero. When the count cannot be read, the banner says so and tries again.
The rail groups the sections by the work. Under Overview are six groups: Content (what the site says and shows), Program (sessions, pitches, speakers and organizations), Registration and tickets, Communications (announcements, scheduled reminders, live updates and the email log), Operations (import, feedback, change requests and data export) and Settings (the event, features, branding, access and system errors). Select a group's name to open or close it. The group that holds the page you are on opens by itself. If you close it, its name still shows the current page under it. The browser remembers which groups you opened or closed. Your tier decides which sections a group shows.
Hide the rail for more room. On a wide screen, select Hide menu at the top of the rail. The rail narrows to the event mark and a Show menu button. Select Show menu to bring the sections back. The page you are on, and any draft on it, do not change. The browser remembers your choice. Hide menu is the first control after the skip link, so Tab reaches it at once.
On a phone, the compact top bar names the event and current section. Open Menu to use the grouped admin sections and account controls in a full-screen sheet. The groups open and close there too. Each destination is a full-width row. The page title and its actions scroll with the form on a phone, so they do not cover the first controls.
Find your way: The tour#
The first time you open the admin panel, a short tour opens above the page. It has one step for each group on the rail that your tier can open, and one step on the edit links on the public site. Move with Next and Back. End it at any step with End tour or the Escape key. The tour does not block the page: You can open a section it names while it stays on screen.
To take the tour again, select Take the tour at the foot of the rail. The browser remembers that you ended the tour, for your account only. A different browser, or another account in the same browser, shows the tour once more.
Who can do what: Operator and staff#
An admin account holds one of two tiers. The rail shows the tier under your address, and it shows only the sections your tier can open. A link you type or follow to a section outside your tier meets a refusal, not the page. The server and the database rules refuse the same requests, so the tier is not only a matter of what the rail shows.
| Tier | Sections | For |
|---|---|---|
| Operator | Everything below, plus Features, Branding, Access, System errors, voiding an invoice, reopening a paid invoice, closing a flagged card payment, changes to the invoice settings, and on Data export the ticket order and admission spreadsheets and the event data package | The person who set the site up and answers for the deployment |
| Staff | Overview, Pages, Sessions, Organizations, Content, Updates, Timeline, Media, Materials, Version history, Unpublished changes, Speakers, Attendees, Badges, Announcements, Scheduled reminders, Live updates, Ticketing, Invoices (write, issue, download, email, make a card payment link, mark paid and duplicate), Import records, Feedback, Email log, Change requests, Event settings, and Data export (spreadsheets of sessions, speakers, organizations, and invoices) | The people who run the event day to day |
Event settings are staff work because dates, venue, places, tracks, the register link, and social handles are content. Two things in there stay with the operator. The sender block: Staff can read the sender email, the sender name, and the reply-to address, and cannot change any of the three, because that is the email identity the deployment was verified against. The social sharing image (seo.defaultOgImagePath): It is branding, and only an operator changes it. A staff save that would change either is refused and the field is named.
An operator grants and revokes access on the Access page (below). At least one operator must always remain, and the server refuses a change that would leave none.
Staff access can end at a set time, for example for a demo account. Until that time, the account is a full staff account. It can edit and publish content like any staff account. An end time does not make an account read-only. There is no read-only tier.
Set the site up: Six steps#
This is the whole visual setup, start to finish. It takes about twenty minutes and you do it once.
- Pick a site style. Settings → Branding, the first control. Six site styles are available. Try your chosen style on your own content. Picking one replaces every value you have not changed yourself, so do this first.
- Add the identity assets. Five slots: The primary logo and the square icon, then the footer logo, the social sharing image, and the favicon behind More image slots. Same picker the Media tab uses. A slot you leave empty falls back to the event name in type, which is a finished look and not a placeholder.
- Set the Main brand colour. One colour. The darker and lighter steps, the rules, the control boundaries, and the whole dark palette are worked out from it. You do not set them, and there is no second colour to set anywhere.
- Preview representative pages. The Page preview beside the controls renders your pages with the draft applied. Look at three: The home page, the schedule on its longest day, and one session. Check each light and dark, and turn the stress test on once.
- Resolve the warnings. Anything the editor flags — a colour pair too close to read, a logo the wrong shape — is named with the pair, the mode, and the measurement. Fix what it names.
- Publish. The live public site restyles with no deploy. This is a config write, not a code change.
Two things are worth knowing before you start. A contrast failure stops a publish, not a save — you can leave a draft broken and come back. And nothing you do here reaches attendees until step 6.
Launch early with tickets and pitches only#
A site can open before the program exists. For example, it can sell early bird tickets and take session pitches months before the event.
- Turn off what is not ready. On Settings → Features, switch off Public schedule, Speaker directory, Sponsor list, Attendee directory and Written updates. Their navigation links leave the header and footer. On Pages, hide any other page that is not ready.
- Write the home page. In the Content editor, keep the opening section short: the event name, dates, place and one paragraph. The Content editor has no switch that hides a whole section. To take a section off the home page, hide or delete each block in it: open the block, select Edit block settings, clear Visible, and publish, or delete the block. A section with no visible block is not shown, and neither is its heading. Three home sections also draw from other lists: the key facts row always shows the event dates, the sponsor strip shows while Sponsor list is on, and the history section shows the past editions in Timeline. To take one of those off, or to remove any section itself, open the home page in Pages and remove the section there. Check every seeded block before launch. Placeholder text that starts with "[Replace]", and the seeded By the numbers statistics (0 attendees expected, 0 sessions planned), show on the public site until you edit or hide them. A button or link that leads to a section you switched off, or to a page that is hidden or does not exist, is not shown. The Content editor says so under Destination as you type. The save still succeeds, and the link appears when you switch the section on or make the page visible.
- Open ticket sales. Publish the reviewed Stripe catalog (see Ticketing). The home page lead and the header then show Get tickets. Before the catalog is published, neither shows a ticket action.
- Open the call for sessions. In Session pitches, an operator sets the deadline as a date and a time on the event's clock, or chooses Open until I close it and closes the call by hand later. The operator then chooses this site's form or an external form (for example, Airtable) and selects Accept session pitches. While the call is open, the home page lead shows Pitch a session. It appears in the home page lead only: the header, the navigation and the footer have no link to it.
Switch the features on again, and publish the pages, when the program is ready. No deploy is needed for any of these steps.
Settings → Branding#
One tab decides how the whole public site looks. Controls on one side, the Page preview on the other rendering your own pages with the draft applied — the client's real pages, not swatches.
What the preview can show you. Pick the page — Home, Schedule, Session, Speakers, or Updates. Switch between Desktop and Phone, which render at real widths so the page's own breakpoints fire inside the frame. Fit the frame or see it at Actual size. Show it Light, Dark, or Compare light and dark side by side. And turn on the stress test: A made-up event name that wraps three times and a day packed with 28 sessions, so you meet a hard page here rather than after a client sends one. Nothing in the stress test is saved. If a colour or a font will not render as you asked, the preview says so.
Make room for the preview. On a screen 1280 pixels wide or more, the controls and the preview sit side by side with a bar between them. Drag the bar, or select it with Tab and use the Left and Right arrow keys, to give one side more room. Shift with an arrow key moves it further. Home and End go to the limits: Each side keeps at least 30 percent of the row. Enter, a double-click, or Reset widths puts it back in the middle. In Compare light and dark, a second bar between the two frames works the same way, and Reset frame widths puts it back. The bars change only how much room each side has. The frame still renders the page at the width you picked, and Fit scales it to its side. Your draft does not change, and nothing is published. The browser remembers both widths. On a narrower screen the preview sits under the controls, and there is no bar. Hide menu on the rail also gives the preview more room.
Work down the controls in this order.
Site style. The base look: A light palette, a dark palette, a type pairing, a shape, a header, and a default set of illustrations. Each one is finished work with a recommended configuration already chosen — you should not have to touch anything else to get a good result. Try the selected style on your event content before publishing. Picking a style replaces every value you have not changed yourself.
Logo and icon. Two slots are asked for here — the primary logo and the square icon — and the other three sit behind More image slots: The footer logo, the social sharing image, and the favicon. All five use the same image picker the Media tab uses.
The square icon also sets the app icon. A phone or a desktop browser shows the app icon when a visitor installs the site. To use your own app icon, upload a square PNG to the square icon slot, from 512 to 4096 pixels on a side. Any other value gives the neutral placeholder icon: An SVG, JPEG, or WebP file, an image that is not square, or a path that is not an uploaded file. The app icon changes at the next deploy. If the site publisher is on for your deployment, it also changes at the next content publish. Publishing the theme alone does not change it. When the placeholder is used, the deploy log names the reason. If the site publisher is on, its log after a content publish names the reason too. DEPLOY_RUNBOOK.md §9 describes the site publisher.
Main brand colour. One colour, as a hex value. The darker and lighter steps, the rules, the control boundaries, and the whole dark palette are worked out from it. Leave it blank to keep the site style's own colour.
Five more choices sit under Advanced. Each site style also answers Long read opening (a drop cap, a standfirst, or plain, for the first paragraph of a page on the Long read template), Quote device (how a quoted sentence is set), Directory style (how the speaker shelf and the attendee index are set), Section boundary (whether a section heading carries its folio, and where), and Table rules (hairline rows, a ruled head, or the full grid). Every style comes with the answer that suits it, and each choice retunes values the style already declares.
Header and schedule. One panel holding two choices. Header style is how the site's identity appears at the top of every page: Each site style offers its own three — a letterhead, a masthead bar, a title page — and comes with the one that suits it. It is a site-wide choice on purpose, because the header is furniture a reader should stop noticing and it stops working the moment it differs page to page. Schedule style is how a session is set out on the schedule: A posted agenda, a hairline row, a departure board, and so on, depending on the style. Both retune the site style rather than inventing a value of their own, so you cannot make a combination the house has not looked at.
Light or dark. Always light, always dark, or follow the reader's own setting. Every site style defines both, so there is no wrong answer here.
Admin colours. The colour of the admin's own rail, buttons and links — this panel, not the public site. By default it follows the Main brand colour, adjusted until white text and the focus ring hold their contrast in light and in dark. Or pick one of six house schemes: navy, graphite, forest, oxblood, teal or plum. The admin changes colour when you publish the theme, and attendees never see it.
Advanced. Everything else the system can do, behind a disclosure, and none of it is needed for a finished site:
- Navigation — Where the navigation sits: Across the top, or down the leading edge on wide screens. One choice, every page, for the same reason the header is site-wide. A single page can overrule it from the page editor.
- Typography — the heading face, picked from the alternates your style offers, and any of the four font roles named outright from the 23-family library.
- Illustrations — a set of small drawings that carry the style's vocabulary, or none. They take the site's own ink and never carry a colour of their own, so they cannot fight your palette.
- Surface and shape — the surface texture, the corners, the spacing, and any extra control your style has of its own.
- Advanced colour settings — any single colour by hand, per mode, with a light tab and a dark tab holding separate values.
Most staff never open Advanced, and that is the intended outcome: Every value in there is normally worked out from the site style and the one brand colour. Setting a colour by hand takes it out of that derivation permanently, so it stops moving when your brand colour moves. Open Advanced colour settings when a brand guide names an exact value that the derived one does not reach; not to explore.
A contrast failure stops a publish, not a save. A draft may hold two colours too close together to read. Publishing that draft is refused, and the message names the pair, the mode, and the ratio it measured. Fix the pair, then publish again.
Fonts come from the bundled open-licence sets, so no page asks an external font service for anything.
Overview#
The admin opens here, for staff and operators alike. The page states how the event is going in short sentences. The server counts every number in them when you open the page or refresh it, so a figure is never a guess made in your browser.
| Sentence | What it counts |
|---|---|
| Accounts | Every account, then the accounts at each registration status: Pending, ticketed, approved, and revoked. |
| Profiles complete | The accounts with a complete profile, out of all accounts. |
| Tickets | Every stored ticket record, then valid, refunded, cancelled, and waiting for details. Native Stripe issues one record per seat; other providers retain their source ticket model. |
| Speakers | Every speaker record, by stage: Draft, invited, accepted, approved, and removed. |
| Sessions | The sessions on the site, and the sessions with unpublished changes. A new session that was never published counts as one with unpublished changes. |
| Unresolved errors | The count only. Operators read the errors themselves on the System errors page. |
Read at. The time under the title says when the server counted the figures, on the event's clock. The figures do not change by themselves. Select Refresh figures to count again. If a refresh fails, the page keeps the figures it has and says when they were read.
Registration funnel. Three stages, each stated as a number of all accounts beside a bar: Accounts, then Ticketed or approved, then Approved. The middle stage counts approved accounts too, because an admin can approve an account that never held a ticket. Revoked accounts count as accounts and in no later stage, and the panel says how many there are. With no accounts, the panel says "No one has signed up yet." and draws no bar.
Content readiness. A table with one row per collection: Pages, Content blocks, Sessions, Organizations, Timeline, and Updates. On the site counts the records attendees can see. Unpublished changes counts the records with a draft that is not published yet, including new records that were never published. A zero is printed as 0, and when nothing at all is on the site the panel says so.
Milestones. When the event settings list milestones or a registration goal, a Milestones panel sets the approved count against the goal and lists each milestone with its date and the days left. You set both under Settings → Event settings, in Milestones and the registration goal. With neither set, the panel is not shown.
Pages#
Every page document (About, Travel, Conduct, and anything else seeded or added) shows its state in the list, in the same three words every editor uses: Draft (never published), Live (published, with nothing pending), or Live with unpublished changes (published, with newer edits saved but not published yet). Create, edit, and publish from here.
Each row also says Visible or Hidden. Visible means a reader can open the page and the navigation links it. Hidden means the page is not on the public site. The word describes the published page, so a page that was never published reads Hidden. When you change a page's visibility and do not publish, a line under the word says that the change waits for a publish. A built-in page whose feature is off on the Features tab also reads Hidden, and a line under it names that feature, because the site hides the page whatever its own setting is.
Three seeded pages start hidden. A fresh site seeds Recap, Guidelines and City guide hidden, because each starts empty and a recap belongs after the event. Write a page, then open it, select Visible, and publish it. A site seeded before this release keeps the visibility it has.
The site navigation is this list. Every page that is visible gets a link in the header, in the order the list shows, under the label you gave it — so adding a page adds its link, hiding a page removes it, and renaming a page renames it in the navigation too. Nobody has to deploy anything for that to happen. One thing keeps a visible page out: a system page whose feature is switched off on the Features tab. An Updates page is still a page when Updates is off — it just has nowhere to send a reader.
A page can carry two names. The navigation label is what the header and the footer print, and every page shares one row, so keep it short — the seeded pages use FAQ, Conduct, Privacy, Terms. The page heading is the heading at the top of the page itself, and it is optional: leave it blank and the page is headed by its label. Set it only where the short label would read oddly as a heading, the way the FAQ page is labelled FAQ and headed Frequently asked questions. The heading is also what the browser tab and a shared link show.
A built-in page has no heading field. Schedule, Speakers, Sponsors, Attendees, Updates, and the home page write their own heading, so the editor states that where the field would be and offers nothing to set. Rename one of these in the navigation label and the browser tab follows the label too.
One item is not a page. The navigation ends with the account control, which the site adds itself. It reads Sign in for a reader who is not signed in and Dashboard for one who is. You cannot remove it or reorder it, and it needs no page document. Multi-role accounts also get an Account menu with attendee, speaker, and admin views. The choice stays in the browser tab for the session; direct role links take precedence. Manage event on attendee and speaker dashboards opens the admin home. Single-role accounts get no switcher or extra admin link. The phone admin Menu sheet carries the same full-width view rows. These controls change navigation only; server access checks still decide every read and write.
The footer lists the same pages. The footer repeats the page list under the same rules, so a page you hide leaves both places at once and a page you rename is renamed in both. Under the page list, the footer shows the links in the home page's Footer links section, one list per group. Edit them in Content, under the home page. They appear in the footer of every page and not in the home page body. Below them the footer names the organization that operates the event and links its support address, both from the event configuration, and lists the event's social accounts if the configuration records any. Each account link is labelled with the service and the handle together, such as Mastodon @eventname, so two accounts on one service can be told apart; an account recorded with no handle is labelled with the service name alone. An event with no social accounts gets no social block, not an empty one. You add and edit the accounts under Settings → Event settings, in the Social accounts panel.
Pick what kind of page this is. Six named tasks, and picking one shapes the page:
| Template | For |
|---|---|
| Standard page | The ordinary page. Start here. |
| Feature first | A page whose subject is the first thing to see. |
| Directory with introduction | A few words, then entries in columns — speakers, sponsors. |
| Long read | Mostly text, meant to be read straight through. |
| Schedule | Dense and time-led, so a long day fits on one screen. |
| Landing page | A front page or a section opener. |
The individual settings behind that — header, arrangement, spacing — are still reachable under Change the individual settings, for the page that genuinely needs to differ. Changing one there clears the template and the template reads Custom from then on, because a page that no longer matches its template should not go on claiming it. A page that has never been given a template or a layout renders the defaults, so nothing changes until you change it.
Where the navigation sits is one choice for the whole site. Across the top, or down the side on wide screens, set on the Branding tab under Advanced → Navigation, because a navigation that wanders between pages stops being the shell that tells a reader where they are. That is the default every page follows.
One page can still differ. Change the individual settings also carries Navigation on this page: Leave it on Follow the site setting — which names what the site is currently set to — or overrule it for this page alone. Use it sparingly and on purpose: A rail beside one long directory is a decision; a rail on three pages out of nine is an accident. It is not part of a template, so setting it leaves the template alone.
What a shared link shows. The server builds the tab title, the search description, and the link preview card for each page from the page's own name plus the event settings, so a link to the travel page shows the travel page's own name rather than the site's front door. Sessions and speaker pages use their own record. The picture on the card is the social sharing image slot on the Branding tab; leave it empty and a neutral placeholder is used. Upload a PNG or a JPEG there, sized 1200 by 630, and not an SVG: the services that draw these cards fetch the file themselves and do not reliably render SVG, so one would leave the card blank. Three things are deliberately left plain: a page that is hidden, a system page whose feature is switched off, and an address that matches no page. Those get the event name alone and are marked so search engines skip them, so an unfinished page cannot be found through search before you publish it. A change reaches previews within a few minutes of the publish.
A system page — home, schedule, speakers, sponsors, attendees, updates — keeps its built-in feature and lets you put sections around it. Each section on one of those pages picks where it is inserted: Before the main feature or After the main feature. Custom pages have no built-in feature, so they have no insertion point to pick.
Sessions#
The schedule itself: Create, edit, publish, and delete a session. Saving writes a draft, and publishing sends it to the public schedule, the same two steps every other content tab uses. Use Preview draft to read the session's public page with the draft applied before you publish it.
Most saved. The panel at the top of the list ranks the sessions by how many attendees saved them to their own schedule, most first. Each row names the session, links to its editor, and gives its day and the count. The counts are the same ones the public schedule shows. A sentence under the table says how many sessions on the site nobody has saved yet; drafts and hidden sessions are not counted, because attendees cannot save them. Before anyone saves a session the panel says "No session has been saved yet." If the sessionBookmarks feature is off (Settings → Features), the panel says so, because the counts cannot change until an operator turns it on again.
Recording link. One optional field on a session, in the Public session panel. Enter the address where attendees can watch the session afterwards. The link must start with http:// or https://; the editor and the server both refuse anything else, so a session cannot store an address a reader's browser would treat as a script. Leave the field empty until the recording is public. A session with a recording link shows Watch the recording on its schedule row and on its session page, and the link opens in a new tab. A session with no link shows nothing at all, so an empty field does not promise a recording later. The link stays on the page after the event, when a past day becomes a back issue and the live controls come off.
No slides or other materials. Some sessions need no slides, for example a conversation or a break-out. Tick This session needs no slides or other materials in the Speaker materials panel, then publish. Speakers on that session are not asked for materials. Their Your next steps list leaves it out, reminders do not name it, and Materials missing and the Materials Coverage panel leave it out too. A speaker whose sessions all carry the mark owes no materials. A speaker on another session that needs materials still owes materials for that session. Speakers can still send materials for a marked session. The box is clear on a new session. The mark does not change the session description: a marked session without a description is still listed under Session description missing.
Organizations#
The organizations the Sponsors page draws: Add, edit, publish, and delete them here, without a script. Saving writes a draft, and publishing sends it to the Sponsors page and to the home page's logo wall.
The list is a table in the order the Sponsors page draws the organizations: by Order, lowest first. Each row gives the name, the page address, the tier, the order, and the state in words. An organization that is saved but not shown on the site also says Hidden. Publish all publishes every organization with a saved draft.
The fields. Name is required. Description is one or two sentences under the name on the organization's page and on the Sponsors page in the list arrangement. Website must start with http:// or https://. Tier is the group heading on the Sponsors page. Order is a number, and lower numbers come first. Logo comes from the media library, or you upload it there. Clear Show this organization when it is published to keep a published organization off the site.
Tiers group by their exact text. Organizations with the same tier text form one group, so "Gold" and "gold" are two groups. The Tier field suggests the tiers already in use. The groups appear in the order of their first organization, and the first group draws the largest logos. To move a tier up, give its organizations lower order numbers.
Each organization has its own page at its page address, /sponsors/ and then the address. It shows the logo, the name, the description, the tier, and a link to the website. The Read more link on the Sponsors page opens it. Use Preview draft to read the page with the draft applied before you publish it.
The page address is set once. A new organization's page address follows its name until you type your own. It uses lowercase letters, digits, and hyphens, and it cannot change after the first save. To change it, delete the organization and add it again. Two organizations cannot share an address: The editor names an address already in use before it saves, and the server refuses a second organization with the same address when you save, not when you publish. Deleting an organization frees its address.
Every save is checked. The editor and the server refuse an empty name, an order that is not a number, and a website that does not start with http:// or https://. The server also refuses a name that is not text, and it checks the logo path: The path must point inside the site's own files, so a web address, a path that starts with /, and a path that contains .. are refused. The server does not check that the file exists. Each refusal names the field, and the editor shows it on that field, the logo's on the Logo field.
Delete removes the live organization and its draft. The logo stays in the media library. The organization's representatives lose access, and an edit waiting for review is removed, so a new organization at the same address starts with no representatives.
Representatives#
A sponsor or partner can keep its own profile current. You give one account access to one organization, and every change that account makes waits for your review.
Add a representative. Open the organization. Under Representatives, type the address the person signs in with and select Add representative. The person must have signed in to the site once, with a confirmed address, before you can add them. One account represents one organization at a time. Then send them the link to /profile/organization on your site. Each addition writes an admin log entry.
What a representative can change. The short description, the longer text about the organization, and the website. Nothing else: the name, the logo, the tier, the order, visibility, the support text, and every other organization stay with staff. The server refuses any other field by name. The website must start with http:// or https://.
Nothing they send is public until you publish it. A representative's save does not touch the organization or its draft. It waits under Edit waiting for review in the organization editor, beside the text in the draft now, and the organizations list marks the row Edit waiting for review. Select Apply to draft to copy it into the draft, then publish as usual. Select Discard this edit to remove it; the organization does not change. Each apply and discard writes an admin log entry. One edit waits for each organization: a representative who sends again adds to their own edit, and while one representative's edit waits, another representative of the same organization is asked to wait until you review it.
Your own edits are kept. Apply writes only the fields the representative changed. If you changed one of those fields in the draft after they sent the edit, apply stops and names the field. Check it, then select Apply and replace staff text to use theirs. Apply also stops while the editor holds unsaved text in the same field: save or undo it first.
Remove a representative. Select Remove beside their address and confirm. The account loses access at once, and an edit it sent that you have not reviewed is discarded. The removal writes an admin log entry. Deleting the person's account also removes their access.
Who sees what. Only staff and operators see who represents an organization. The representative's address never reaches the public page, the draft, or a published version.
Content (Pages → Content editor)#
Filling in what a page's blocks say is a separate step from shaping the page: Pick a page, then a section, then the block inside it. A page's structure — its sections, which block types each allows, its default blocks — is set in the page editor above; this tree of screens is for the block content. All eleven block types are editable here.
A fact is a term and a description. The fact block carries three parts: The term ("Where", "Who", "Format"), the fact itself (the hall's name, the audience), and one optional line under it (the address, a way to get there). It asks for no source and no finding, because a venue counts nothing. A run of facts renders as one ruled list of term and description pairs. Use it for anything that is true and is not a number.
A quote is one sentence and who said it. The quote block carries the sentence, without quotation marks, and an optional attribution. The site draws the marks and the rules in every site style, either a large opening mark or a pair around the sentence. One pull quote per page: The first quote block on a page, in reading order, is set as the pull quote, and any quote after it is set as a plain quotation in the body, with its attribution under it. Nothing you wrote is dropped; the second one does not take the page's one expressive frame.
The home page's Key facts section is a group of cards. It answers the questions a first-time reader arrives with. Each fact or statistic in that section opens a card. Each list item after one is a line on that card, so a card is one fact and then whatever a reader also needs to know beside it. Move a line under a different fact and it moves to that card. On a wide screen the cards sit in a row of three; on a phone they stack into one plain list, in the same order.
You place it, like any other section. Move it up or down the home page's section list and the group moves with it. Set it to appear before or after the main feature and it obeys that too. The seed puts it near the top, under the opening, which is where a reader looks first.
A fresh site seeds three cards: When, with the dates from your event settings; Where, with the venue's name and its address under it, and one line for the nearest transit; and Who, which you write. The When card follows your event settings for as long as you have not edited it: Move a day in Settings and the card moves with it, so it never disagrees with the Dates list beside it. Remove every day and the card goes back to the placeholder the seed writes for an event with no dates. Edit the card and it reads as you wrote it from then on. Add a statistic to open a card on a figure that is evidence: A statistic has to name where its number came from, and there is no source to name for the name of a hall, which is why a place or an audience is a fact and not a statistic. Empty the section, or fill it only with block types this section does not draw, and no heading is printed at all.
The home page's Sponsors section is the logo wall, not a list of blocks. It holds one line of text, and under it the site draws the same tiered logo wall the Sponsors page draws, from the same published organizations in the same tier order. Add or reorder organizations in the Organizations list; nothing about them is edited here.
It is a section like any other, so you place it. Move it up or down the home page's section list and the wall moves with it. Set it to appear before or after the main feature and it obeys that too. The section draws nothing at all when no organization is published yet, when the Sponsors feature is switched off, or when you delete the section from the home page. Deleting the section is how you take the wall off the home page and keep the Sponsors page.
The home page's History section is your text, then the timeline. Write the section's own words and pictures here, as for any other section. Under them the site draws the past editions from the Timeline list, oldest first. You edit the editions under Timeline, not here. Move the section and the list moves with it. Delete the section and the list is gone from the site too.
A sponsor package is one thing a sponsor can support. The sponsor package block carries a name and what the package includes, both required, and an optional price and limit. Write the price as it should read, with its currency. The limit is how many sponsors can take the package, as a whole number of 1 or more: The page shows "Open to 3 sponsors", and shows nothing for no limit. The save refuses any other number. The Sponsors page seeds one section for packages, Sponsorship packages, after the logo wall. It is not shown until it holds a package, so an empty section never reaches a visitor. A site whose Sponsors page was edited before this section existed keeps its own sections; add the section in Pages if you want it.
Formatted text uses a toolbar. Rich text, FAQ answers, and sponsor package benefits open a visual editor. Select text, then use the toolbar for headings, emphasis, lists, quotations, code, dividers, or links. The standard Bold, Italic, Underline, Undo, and Redo keyboard shortcuts work in the text area. Every toolbar action is also a labeled button that you can reach with Tab. The character count is below the editor.
The editor accepts web links, email links, phone links, relative links, and links to another place on the same page. It removes unsupported formatting when you paste or open saved text and tells you when it did so. Images, embedded video, and formulas are not rich text formats. Add an image with an Image block instead. Save and publish use the same draft and live revision flow as every other content block.
Edit a section from the site. While you are signed in as an admin, each section on a public page shows an Edit section link beside its heading. On a section with no visible heading, the link comes after the section's content. The link opens that section's blocks here, the same screen you reach from Content → page → section. Visitors and signed-in accounts that are not admins do not see the link. On the home page, the opening section's link is the last line of the opening. The Sponsors link opens the line of text above the logo wall. You change the wall itself in the Organizations list. A page's own feature, such as the schedule grid or a directory, has no link: Edit it in its own section of the rail. An empty section is not shown on the site, so it has no link either.
A statistic needs four things, and the editor will not save one without them: The finding in words ("Two thirds of sessions are workshops", not "Session types"), what the number counts and over what period, where the number came from and the date you read it, and a line describing the finding for a screen reader. A stat block saved before this rule existed keeps working; the next time you edit one, you will be asked to fill the four parts in.
Updates#
Every update on the site's Updates page. Each row gives the update's title, its state in the three words every editor uses, its date, its category, and its place in the list: Featured, Featured and pinned, Pinned, or By date. An update that is not shown when published also carries Hidden. The list is in the order the public page uses: Pinned updates first, then the newest date first, and undated updates last. Short notices for the dashboard card are a different feed, under Live updates.
Write an update opens an empty editor. Give it a title and the text. Save draft writes a draft: The public page does not show it. Save and publish saves and then publishes it, and the public page shows it at once. Publish all on the list publishes every update with a saved draft that is not live. Press Enter in a one-line field to save a draft.
The date is the date readers see, on the event's clock. It is set in the event's time zone (Settings → Event settings), the same clock the public page uses for the date under the title. Leave it empty for an undated update; an undated update goes after every dated one. A date with a part missing is refused when you save, so finish it or clear the field. The date does not delay publishing. An update dated next month goes live when you publish it, and it shows next month's date.
Pin an update to hold it at the top of the list, above newer updates. Clear Show this update when it is published to publish an update that the public page does not show.
A category is one or two words, such as Travel or Program, shown as a tag beside the update's title on the Updates page. It takes up to 24 characters on one line, so the tag fits on a phone. Leave it empty for no tag. The field suggests the categories other updates already use, so one topic keeps one spelling.
Feature an update to put it at the head of the page. The Updates page sets the featured update first, under its own heading, Featured, with a larger title and its opening lines. If more than one update is featured, the one that comes first in the list leads: A pinned update before an unpinned one, then the newest date. The other featured updates stay in their usual places. To change the lead, clear Feature this update at the head of the list on the update that leads now.
A picture and content blocks. An update can also carry a picture and content blocks. This editor does not change them. When an update has them, the editor says so, and a save keeps them as they are.
Delete this update removes the live update and its draft together. Its version history stays. If Updates is off under Features, the list and the editor say so, because the public site does not show the page. Only an operator can turn it on.
Timeline#
The past editions of the event: Add, edit, publish, and delete them here. The home page's History section lists them under its own text. Saving writes a draft, and publishing sends the entry to the home page.
The list gives each entry's title, year, and id, and its state in words, oldest first. An entry that is saved but not shown on the site also says Hidden. Publish all publishes every entry that is not live.
The fields. Year is four digits, from 1900 to 2100. The site shows it beside the title and orders the list by it, oldest first; two entries in one year go in title order. Title is required: What that edition is remembered for, in a few words, on one line of up to 120 characters. Leave the year out of it. Description is optional: One or two sentences of plain text, up to 600 characters, shown as one paragraph. Clear Show this entry when it is published to keep a published entry off the site.
Every save is checked. The editor and the server refuse a year that is not four digits in range, a missing title, and text that is too long. The refusal names the field.
There is no draft preview. The entries appear only on the home page, and the home page can send an account with no display name to its profile, so the editor offers no preview link. Save and publish, then read the home page.
The list appears only in the History section. If the home page has no section with the id history, published entries do not appear anywhere, and the Timeline list says so. Add a section with that id to the home page under Pages.
Delete removes the live entry and its draft together. Its version history stays.
Unpublished changes#
Everything that is saved and not yet on the site, in one place, under Content on the rail. Staff and operators both open it. The page lists pages, content blocks, sessions, organizations, updates, and timeline entries. Speaker edits are not listed here: You review them on each speaker's page.
One count, two places. The sentence under the title, such as "5 unpublished changes: 2 content blocks, 1 page, 2 sessions.", is the same sentence the banner shows on every other admin page. Both read the same saved drafts, so they always agree. A save in any editor raises the count at once, and a publish lowers it at once.
One table per kind of record. Each row names the record, with its id under the name, and gives its state (Draft for a record that was never published, Live with unpublished changes for one that was), when it was saved on the event's clock, and which account saved it. A record set to hidden also reads Hidden. The newest save comes first. The name links to the record's editor, where the admin has an editor for that kind of record.
Three ways to publish.
- Publish all at the top publishes every unpublished change on the site.
- The button on each table, such as Publish 2 content blocks, publishes that table only. One table takes at most 2,000 changes at a time; past that, the table asks you to use Publish all.
- Resume publish on a failed run, below.
While one publish runs, the other publish buttons do nothing. When the list cannot be refreshed, the button on each table waits until it can: a record in a stale list may have been published since, and sending it again would publish it twice. Publish all still works, because it publishes what is unpublished when you press it. The result is stated under the title. A change that was edited while it was being published stays unpublished, and the result names it, then names what did go out.
Recent publishes. Under the tables, the last 10 publish runs and the 20 newest runs still marked Failed, newest first. Running means the run is still publishing. Done means it finished; the line says how many changes it published and how many it skipped. Failed means it stopped part-way; the line says how many changes went out before it stopped, and the run shows the error. A run with no progress for 90 minutes is marked Failed by the maintenance sweep, which runs every 30 minutes. A failed run stays in the list until you resume it, however many finished runs follow it. When more than 20 runs are marked Failed, the list shows the 20 newest. Resume publish publishes the current saved version of each record the run did not reach, so a record edited since the run failed goes out as it reads now.
Media#
A browsable library, split by namespace because each answers a different question:
cms-images/— images used inside pages and posts. Client-writable through this tab.branding/— the logo slots, backed by the same reusable image picker the Branding tab uses.speaker-photos/— set from the speaker editor, not uploaded loose here.
Upload, edit alt text, and delete from the library. A delete warns you first if anything currently on the site references the asset — do not dismiss that warning without checking what it names.
Display sizes. When you upload a PNG, JPEG or WebP image here, or a speaker headshot, the site makes smaller copies of it a few seconds later. The copies are WebP files 320, 640, 1280 and 1920 pixels wide, but only the widths narrower than your upload. A small upload gets fewer copies, or none. Speaker portraits, sponsor logos, the lead image and the home hero use them, so a phone downloads a small file and not your full upload. Your upload stays as it is. The site still uses it when a copy does not exist.
- The copies have no camera data. Location (GPS), camera make and other EXIF details are removed, and the picture is turned upright first.
- The site makes no copies of SVG or GIF files, of animated images, of files over 5 MB, or of images over 40 megapixels. A file that is not really an image gets no copies. The page then shows your upload, as before.
- If an image file is replaced at the same address, the copies of the old picture are removed and new ones are made.
- An image uploaded before this feature has no copies. The page shows the original. To get copies, upload the image again and choose the new one.
- When you delete an image, its copies are deleted too.
Branding needs an operator. An upload into branding/, and a delete or an alt-text edit of a branding asset or of any asset a Branding slot or the social sharing image uses, is refused for a staff account, with or without the delete warning dismissed. A staff account sees the Branding drawer read-only, with a note that an operator manages branding files. Everything else in the library is staff work.
Speakers#
The canonical speaker list, with each record's pipeline status and the entry points to create, edit, invite, and review pending edits. This list reads the real speaker records, not the public-facing projection, on purpose: An admin needs to see speakers who are not published yet — drafts, outstanding invites, soft-deleted records.
Pipeline states, top to bottom of what a record moves through:
| Status | What it means | Action available |
|---|---|---|
| Not invited | Record exists, no invite sent | Invite |
| Invite sent | Emailed; not yet accepted | Resend |
| Accepted / published | Speaker signed in and (optionally) edited their own profile | — |
Pending-edits review. A speaker can propose changes to their own profile after accepting an invite. Those queue rather than landing on the live record, until an admin approves them. Review and approve (or reject) from the speaker's row — the queued fields are named right there, so you are never approving a change blind.
Create/edit form. Every field except the account link and the invite token is editable here. Those two move together in a single server-side transaction as part of the invite/accept pipeline and are never part of an admin payload, by design: A leaked value there is rejected by name on the server, not silently accepted.
One speaker record is the source of truth. If a name is wrong in three places on the public site, fix the speaker record — do not patch each place it appears.
Speaker email wording#
Change the words of two emails: the Invitation a speaker gets when you send or resend an invitation, and the Acceptance confirmation a speaker gets once, after they accept. Select Edit email wording at the top of the Speakers list. Staff and operators can use this page.
Each message has its own panel with Subject, HTML body, and Plain text body. The panel says whether the shipped wording or saved wording is in use, and who saved it and when. It lists the tokens the message may use. Event tokens such as {{event_name}} also work. The links stay fixed:
- The invitation's HTML body and plain text body must each keep
{{invite_url}}, the private link that accepts the invitation. - The acceptance confirmation's two bodies must each keep
{{profile_wizard_url}}, the link to the speaker's profile.
Save wording checks the words on the server first. The server refuses a token the message does not know, a broken placeholder, an empty field, and a body without its link, and names the field. Nothing is saved, and the cursor moves to that field. The next invitation or acceptance email uses the saved wording; there is no delay.
Preview shows the exact subject, the HTML version in a safe frame, the plain text version, and the HTML source, with the legal footer the email gets. It uses the words in the form, saved or not, and says which. Sample values fill the tokens: the name "Sample Speaker", your own address as the contact, and a sample link in place of the private one. A change to the words clears the preview. The preview says so if the event has no public site address, because the links then do not work.
Shipped wording shows the wording that ships with the product. Copy the shipped wording into the form puts it in the form without saving it. Use the shipped wording deletes the saved wording, so every later email uses the shipped wording.
If saved wording fails its check (for example, someone removed the link outside this page), speakers get the shipped wording and the panel says why. Each save and each reset writes an admin log entry. A historical demo refuses save and reset; the preview still works.
Speaker reminders#
Email accepted speakers about the items they still owe. Select Send reminders at the top of the Speakers list, or Remind on the row of one accepted or approved speaker. Staff and operators can use this page.
What counts as missing. The page uses the same rule as the speaker's own Your next steps list:
- Biography and Headshot: The field is empty. A change the speaker sent for review counts as done.
- Session description: A published session the speaker is on has no description text.
- Materials: A published session the speaker is on has no pending or approved material. A rejected material does not count. A session marked as needing no slides or other materials never counts (see Sessions).
Only published sessions that are not hidden count. Only speakers who accepted an invitation are listed. A speaker who is not invited yet, or whose invitation is still open, gets the invitation instead.
Materials waiting for review. A pending material counts as sent, because the speaker has done their part. Staff have not approved it yet, so the page also lists it apart. A session with a pending material and no approved material shows on the speaker's row as Waiting for review. One approved material ends the wait. A session marked as needing no materials is never missing them, but an upload to it is still listed here until staff review it. No reminder names it.
Choose speakers. Show narrows the list: Anything missing (the default), one item, Materials waiting for review, or Every accepted speaker. The choice stays in the page address. Each row names the missing items with the session titles, and says when the speaker was last reminded and about what. Tick one speaker, or select Select all shown. A change to Show clears the selection. Items to include picks the items a reminder may name. Each speaker's message names only the chosen items that speaker still owes.
Preview. Preview reminders shows the message each speaker gets: the address, the subject, the HTML version in a safe frame, and the plain text version. Choose a speaker under Recipient to read their copy. The preview also names each selected speaker who gets no reminder and why: No email address, nothing missing in the chosen items, a reminder in the last 24 hours, or no accepted invitation. A change to the selection or the items clears the preview.
Send. Send 3 reminders (the number is the recipient count) opens a confirmation that names the speakers. The server sends only the messages the preview showed:
- One reminder per click. Each preview has its own id. The server records it for each speaker before the email goes. If you press Send twice, or the connection drops and the browser tries again, nothing more is sent.
- At most one reminder per speaker in 24 hours. A speaker reminded less than 24 hours ago is left out, and the row says when the next reminder is possible.
- The wording the preview showed. If someone saves new reminder wording after your preview, the server refuses the send. Preview again, then send.
- The messages the preview showed. If a speaker's address, a missing item, a session title, or the site address changes after your preview, the server refuses the whole send. Preview again, then send.
- A message that could not be emailed is reported after the send. Preview again to retry it.
- A message the provider may have accepted. If the email provider stops answering after it took the message, the send reports that the speaker may have the reminder. The site does not send that speaker another reminder for 24 hours. Check the Email log.
Every reminder goes through the event's email provider and appears in the Email log with its body. Each send writes one admin log entry with your account, the items, and the speakers who got a reminder. If a send stops part way, the entry is still written, names the speakers emailed so far, and is marked incomplete. A historical demo refuses the send.
Reminder wording. The Reminder wording panel edits the subject, the HTML body, and the plain text body. Each missing item is one line inside a condition, for example {{#if materials_sessions}}…{{/if}}, so a line appears only when the speaker owes that item. {{description_sessions}} and {{materials_sessions}} hold the session titles. Both bodies must keep {{dashboard_url}}, the link to the speaker's dashboard. The server refuses a token the template does not know and says which one. Use the shipped wording deletes the saved wording. Each save and each reset writes an admin log entry. Save, then preview, to read the new wording as each speaker gets it.
Saving or resetting the wording pauses scheduled reminders until you approve them again (see Scheduled reminders below). The panel says so above Save wording.
Export this list as CSV saves the speakers the current Show choice lists, one row per speaker, with the name, the email address, the status, the missing items, the sessions without a description, the sessions without materials, the sessions with materials waiting for review, and the last reminder. Use it with your own mailing tool. A cell that starts like a spreadsheet formula is escaped. Each export writes an admin log entry first. If that entry cannot be written, no file is made.
Scheduled reminders#
Send reminders on a schedule: to speakers before a due date, and to attendees before the event starts. Open Scheduled reminders under Communications on the rail, or Scheduled reminders at the top of the Speaker reminders page. Staff and operators can use this page. Each schedule is off until you preview it and approve it. The speaker schedule is described first, then the attendee schedule.
Settings. Under Speakers:
- Days before the due date: One reminder for each number, from 1 to 365 days, at most six. The default is 14, 7 and 2 days.
- Due date: The date and time the items are due, on the event's clock. The field names the event's time zone. Approving needs a due date.
- Send time: The time of day each reminder goes, on the event's clock. The default is 10:00.
- Items to remind about: Biography, Headshot, Session description, Materials. All four by default.
Select Save settings to store them.
Who gets a reminder. Each accepted speaker with an email address who still owes one of the chosen items. The rule is the same as on the Speaker reminders page. Each message names only the chosen items that speaker still owes, and says the due date. A speaker who finishes gets no more reminders.
When. A reminder for 7 days goes 7 calendar days before the due date, at the send time. Across a daylight saving change it still goes at the send time on the clock people read. The site checks once an hour. If a check is missed, the next one sends the reminder, until the next reminder day starts. Then the earlier reminder is dropped, so nobody gets the 14-day reminder 3 days before. Nothing sends from 8:00 PM to 8:00 AM on the event's clock; a reminder due then waits for 8:00 AM. A check that reaches 8:00 PM part way stops, and the rest of the speakers get the reminder the next morning.
At most one per reminder day, and one in 24 hours. A speaker gets each reminder day's message once. The 24-hour limit of the Speaker reminders page applies in both directions: A speaker reminded by hand in the last 24 hours waits until the 24 hours end, and a scheduled reminder stops a reminder by hand for 24 hours. A message the email provider may have accepted counts as sent and is not sent again. Check the Email log.
Preview, then approve. Save the settings, then select Preview. The page shows one sample message for each reminder day, as the first speaker who owes an item would get it. If nobody owes an item, it uses a sample speaker. Select Approve and turn on to turn the schedule on with that wording and those settings. If the wording or the settings changed after your preview, the server refuses. Preview again, then approve.
Paused. The schedule pauses, and says why, in these cases:
- Someone saves or resets the reminder wording on the Speaker reminders page.
- A deploy changes the shipped wording that the schedule sends.
- Someone saves different settings while the schedule is on.
A paused schedule sends nothing. Preview, then approve to turn it on again. When a check finds wording you did not approve, it also tells the operator.
Next run. The panel says when the next reminder goes, with the time zone named, which reminder day it is, and who it would reach. It names the speakers who wait on the 24-hour limit, and when they can get one. It also says what the last check did.
Turn off stops the schedule. A check in progress stops before its next speaker. A pause or a settings change does the same. Nothing sends until you approve it again.
Attendee reminders#
Under Attendees, the site emails registered attendees before the event starts: the days left, the start time, the place and their ticket link.
Who gets one. The Registered attendees audience of the Announcements page: approved attendees and people who claimed a ticket. A revoked account, an address that unsubscribed, and an account with no usable address get none. Each address gets one copy.
Settings.
- Days before the event: One reminder for each number, from 1 to 365 days, at most six. The default is 7 and 1 days. The days count back from the start time of the first event day.
- Attendee send time: The time of day each reminder goes, on the event's clock. The default is 10:00.
- Attendee reminder subject and Attendee reminder message: Plain text. A blank line starts a new paragraph.
{{days_until}}is the only placeholder. It becomes the days left when the reminder goes, for example "7 days" or "1 day". On the morning of the first event day it reads "less than a day". The server refuses any other placeholder and names it.
Select Save attendee settings to store them. Wording the same as the shipped wording stays the shipped wording.
What the site adds. After your message, the site adds the start date and time, the venue, and a button to the ticket link. They come from Event settings, and the panel shows them. The ticket link goes to Your tickets (/tickets/mine) when the site sells tickets through Stripe, and to the dashboard (/dashboard) otherwise. If the event has no venue, the reminder leaves the place out.
Sent as an announcement. An attendee reminder goes the same way as announcement email. It goes on the announcement stream, with an unsubscribe link and one-click unsubscribe headers. An address that unsubscribed gets no reminder. An address that unsubscribes during a send gets no more of it. Each reminder day shows in the Announcements send history as a Scheduled attendee reminder. You cannot continue it from there; the schedule does. Set up the announcement stream and send a test announcement before you turn the attendee schedule on (see docs/POSTMARK_PROVISIONING.md, section 2a).
When. The rules are the same as for speakers: calendar days at the send time, a missed check made up until the next reminder day starts, and nothing from 8:00 PM to 8:00 AM. A send that reaches 8:00 PM part way stops, and the next morning's check sends the rest. Nobody gets a second copy. A send cut off when the next reminder day starts shows Stopped in the history, and the rest of it is not sent.
At most one per reminder day. Each address gets each reminder day's email once. A person who registers after a reminder day's email went out gets the next one. The days in the email are the days left when that reminder day's send began.
Preview, then approve. Save, then select Preview attendee reminders. The page shows one sample for each reminder day, with the days left as they will read, the added facts, and an unsubscribe link that changes nothing. It says how many attendees it would reach and how many unsubscribed. Select Approve and turn on attendee reminders to turn the schedule on with that wording, those settings and those facts. If any of them changed after your preview, the server refuses. Preview again, then approve. If announcement email cannot be sent, for example because the announcement stream is not set up, the preview says why and you cannot approve.
Paused. The attendee schedule pauses, and says why, in these cases:
- Someone saves different wording or settings while it is on.
- A deploy changes the shipped wording or the announcement email layout.
- The event start, the venue or the ticket link changes in Event settings.
A paused schedule sends nothing. Preview, then approve to turn it on again. When a check finds a change, it also tells the operator. If you approve again during a reminder day whose send has begun, with new wording, new settings or new facts, the rest of that send stops. The next reminder day uses the new approval.
Next run. The panel says when the next reminder goes, with the time zone named, which reminder day it is, and how many attendees it would reach. While a reminder day's send is in progress, it says how many addresses are left in that send. It does not list addresses. It also says what the last check did.
Turn off attendee reminders stops the schedule. A check in progress stops before its next group of 200 addresses, and the rest of that reminder day's send is marked Stopped. A pause does the same.
If the announcement stream changes during a send. The rest of that reminder day's send stops and shows Stopped, and the operator is told. Any other fault with the stream also tells the operator; the next check tries again.
Time limit. Each check runs the speaker and the attendee schedules side by side, for up to eight minutes. A check that reaches the limit stops before its next speaker or its next group of 200 addresses, and the next check sends the rest.
The schedule does nothing on a historical demo, and nothing after the last event day. Every approval, settings save, pause, and turn-off writes an admin log entry. Each check that emails anyone writes one admin log entry that names the speakers.
Attendees#
The full attendee list with registration status and a search/filter. What action is available depends on the current status:
- Pending / ticketed → Approve
- Approved → Revoke. Approving again is available only when the original approval came from a ticket; re-approving records the approval as an admin decision so a later revoke behaves correctly.
- Revoked → Approve. That is the only way out of revoked.
Approve and revoke always go through the server endpoints. Every registration field involved is server-owned and not editable directly, even for the account's own owner.
Export the list. The button in the title band reads Export 42 attendees, where the number is the rows on screen after your search and status filter. It saves a CSV file with one row per account, in the order the page lists them. The file has nine columns, and nothing else leaves the site:
| Column | What it holds |
|---|---|
| Name | The display name |
| The sign-in address | |
| Organization | The organization on the profile |
| Role | The role on the profile (the job title, not an access level) |
| Registration status | The stored value: pending, ticketed, approved, or revoked |
| Badges | The configured badge labels, then the custom badges while that feature is on |
| Past attendance | The editions an organizer recorded on the account |
| Social handles | label: handle pairs, sorted by label |
| Profile visibility | The stored value: public, attendees_only, or private |
A cell with more than one entry joins them with a semicolon. The file carries no account id, pronouns, bio, photo, or dates. The status and visibility columns keep the stored words, so a spreadsheet filter on them stays stable.
Formulas stay text. A spreadsheet runs a cell that starts with =, +, -, or @ as a formula. In the file, such a cell starts with an apostrophe, and so does a cell that starts with a tab or a carriage return, or with spaces and then one of those four characters. The spreadsheet shows the value as text.
Every export is recorded. The server writes an admin log entry with your address, the number of rows, the status filter, and whether a search narrowed the list. It never records the search text, because that text can name a person. If the entry cannot be written, the server refuses the export and makes no file. The server keeps no copy of the file. One export holds at most 10,000 attendees and 10 MB; a larger one is refused before anything is recorded, and the message asks you to narrow the filter.
Past attendance. Select Edit record on a row to open that account's record. Past attendance takes one edition per line, such as a year: at most 20 editions of up to 40 characters each, with no repeats. Select Save record to store it. Attendees cannot change this list, and it never appears in the directory. It is a column in the export, and the row shows it under the address. Each save writes an admin log entry.
Delete an account. In the record, select Delete account, read what the delete removes, and select Delete this account. The delete removes the account, its directory profile, its shared schedule, and its access as an organization representative, and releases its ticket claims, in one step, so the person leaves the directory at once. It then removes the sign-in, the saved sessions (and lowers each session's saved count), the private notes, the profile photo, the change requests the person sent, and any organization edit the person sent that is still waiting for review. The ticket record stays, unclaimed. Sent email records, feedback, session reactions, and the admin log stay too. The delete writes an admin log entry with your address, and it cannot be undone. A person whose account was deleted can sign in again later; they get a new pending account with no history.
The server refuses a delete in four cases, and says why:
- Your own account.
- An account with operator or staff access. An operator removes that access on the Access page first.
- An account linked to a speaker. Delete the speaker record first, in Speakers.
- An account that holds more than 495 claimed tickets. The refusal names the count, because one delete can release at most 495.
If a delete stops part way. When the account has left the directory but some of its data did not clear, the page keeps a notice with Try the delete again. The page does the same when the answer does not arrive, for example after a timeout or a dropped connection, because the delete may have started. Select it until the page says the account is deleted. Each retry clears only what is left, and records itself in the admin log. For up to an hour after the delete, the person's open session can still claim a ticket; a retry also releases a ticket claim made after the delete. A refusal made before anything was deleted, such as a speaker-linked account, shows in the record itself.
Ticketing#
The workspace follows the deployment's configured provider (CLIENT_ONBOARDING.md §3 item 5):
- Stripe: Native event-site selection, Stripe-hosted payment, and the organizer controls below. Merchant setup and opening live sales require the Stripe launch and support runbook.
- Eventbrite: Provider status shows webhook registration state and recent sync activity.
- Manual / CSV import: Upload a spreadsheet, map its columns to attendee fields, preview the import as a dry run, then commit it.
- None: Nothing to configure.
Ticket records are server-only in Firestore. Every list, search, import, and commerce action goes through an authenticated endpoint; even an admin browser cannot read the commerce collections directly.
Stripe sales and catalog#
The Stripe sales panel identifies Test mode or Live payments, the publication/pause state, and held/issued seats. An operator opens Configure ticket catalog to set currency, capacity, prices, quantity limits, UTC sale windows, tax treatment, and refund policy. Currency becomes fixed after the first save. Every type grants full-event access; an early-bird type changes its offer and sale window, not its access rights. Hidden types stay out of public purchase selection.
Keep Pause new sales checked while configuring. Check Commercial terms are approved for this event only after the organizer approves the merchant, refund/support policy, tax treatment, and absorbed processing fees. Use Publish the catalog and Save catalog after readiness is complete. Review reopening sales and Review pausing sales show a confirmation and require a reason. Pausing stops new purchases while existing payments and tickets continue to reconcile.
Orders, admission, and support#
Orders searches purchaser email or order reference and filters by Payment state and Refund state. Open the order reference for Order details. Admission tickets searches attendee email or ticket code and filters by ticket type, admission, claim, and check-in state. Load more orders and Load more tickets continue the current submitted search.
In an order, staff can Resync this order, Resend ticket confirmation, or Correct attendee. Claimed or checked-in tickets permit name corrections but keep their email locked. There is no in-app ticket transfer. The purchaser owns payment details; each attendee claims their assigned seat using the existing verified-email form. The confirmation link prefills the order reference without claiming automatically. Claimed attendees can open /tickets/mine to Print tickets, which includes an opaque code staff can look up.
Select the exact tickets before Review ticket cancellation. The confirmation names the order, original amount, selected attendees/codes, and admission consequences; add a reason before confirming. Cancellation removes admission without refunding money or returning sold seats to capacity. An operator must cancel a ticket that has already been checked in.
Money refunds happen through Open this payment in Stripe Dashboard, using the organizer's authorized Stripe account. Refund history records provider results. A succeeded full refund invalidates all tickets. For a succeeded partial refund, an operator chooses Refund to allocate, then selects affected tickets or explicitly chooses Keep every ticket valid for a price adjustment. Review refund allocation confirms that decision without creating a money refund. A dispute appears under Payment disputes for merchant review; it does not automatically cancel admission. Reasons and actions appear in Audit trail.
For complimentary seats, an operator opens Issue complimentary admission, enters each attendee and a reason, and uses Review complimentary tickets before the final issue confirmation. Comps consume real event/type capacity. At the door, staff enter or scan a human-readable code in Ticket code for check-in, then select Check in attendee. A repeat reports already checked in. Only an operator can use Review check-in correction → Undo recorded check-in, with a reason.
If a payment response is lost, the purchaser uses Resume existing checkout or Recover existing checkout. A declined attempt can continue through its existing open Stripe session. Returning from Stripe, closing its window, or seeing a local countdown does not fulfill or cancel an order. Use the support matrix before suggesting another payment, refund, resend, or admission change.
Invoices#
Invoices under Registration and tickets is where staff bill sponsors and partners. Write a draft, issue it, download or email its PDF, and record the payment. When the site takes payments through Stripe, the sponsor can also pay by card through a link.
Invoices are private. The database refuses every browser read and write, for operators and staff alike, and every change goes through the server. Each change writes a row to the audit log.
Write a draft#
- Open Invoices, then New invoice.
- Under Organization, choose the organization to bill. The page fills Name to bill with its name. If the organization's tier matches the name of a sponsor package on the site, the first line becomes that package, with the first number in the package's price as the unit price. Lines you already wrote are kept.
- Check the contact name, billing email, billing address and reference. Only Name to bill is required.
- Write the lines. Each line has a description, a whole-number quantity and a unit price. Type prices as digits, such as
1250.00; commas between thousands are fine. An invoice holds up to 50 lines. A line you leave blank is left out when you save. - Add discounts and tax with Add a discount and Add a tax. Type each amount by hand. A discount subtracts and a tax adds. There are no rates. An invoice holds up to 10 of these rows.
- Set a due date if you want one. It is a date on the event's clock.
- Select Save draft.
The totals under the form change as you type. They are a preview: The server computes the same figures again when you save, and the saved figures are the ones that count. A total below zero is refused. Reduce a discount.
A draft has no number. The number (INV-0001, INV-0002, and so on, with no gaps) is given when the invoice is issued, so deleting a draft never leaves a gap. Each invoice uses one currency: The ticket catalog's currency, then the currency in the invoice settings, then US dollars. A draft keeps the currency it was created with.
If someone else saved the same draft after you opened it, your save is refused. Select Reload the saved invoice, then make your change again.
To remove a draft, open it and select Delete this draft. Only a draft can be deleted.
Issue an invoice#
Issuing gives a draft its number and makes it a real invoice. It cannot be undone.
- Save the draft. Issue uses the saved draft, not the form. If the form has changes, the page asks you to save first.
- Select Issue invoice. The page names the next number, such as
INV-0004. - Select Issue this invoice.
The invoice takes the next number, today's date on the event's clock as its issue date, and a copy of the issuer name, address, email, tax ID and payment instructions from the invoice settings. The server computes the totals again and keeps them. After that the invoice cannot change: later edits to the settings or to the organization do not reach it, and its PDF always shows the same details.
Issue is refused, and the page names the reason, when the draft has no line, when its due date is before the issue date, or when the invoice settings have no issuer name. An operator fills in the settings.
Numbers have no gaps. Two people who issue at the same moment get two numbers in a row. A number is used only when an issue succeeds, so a refused issue uses none.
Download the PDF#
Open an issued, paid or void invoice and select Download PDF. The browser saves it as the invoice number, such as INV-0004.pdf. The PDF shows both parties, the dates, every line, the discounts and tax, the total, how to pay, and the notes. A paid invoice says when and how it was paid. A void invoice says VOID and the reason, and has no payment instructions. A draft has no PDF.
The PDF uses a standard font. A character it cannot draw, such as a letter in a non-Latin script, is dropped or replaced with the nearest plain letter.
Email the invoice#
Staff can email an issued or paid invoice. The PDF goes as an attachment to the billing email on the invoice. You cannot send it to another address, but you can add one copy.
- Open the invoice and select Email invoice.
- Optional: In Copy to, type one more address, such as your finance team. It cannot be the billing email.
- Optional: In Note, write a short message. It appears in the email under the line that names the invoice. It can hold up to 1,000 characters.
- Select Preview email. The page shows the address, the copy, where replies go, the subject, the file name and the text of the email.
- Read the preview, then select Send to and the billing email.
Replies go to the issuer email in the invoice settings, when it has one. If you change the copy address or the note, preview the email again. If the invoice changes after the preview, for example someone marks it paid, the send is refused. Preview it again.
The record shows each email under Emails sent: when, to which addresses, and whether it was sent. Each email is also in the Email log and the audit log. The audit log does not hold the addresses or the note.
If an address got this invoice less than 15 minutes ago, the preview says so. To send it again, tick Send it again anyway. This stops a double send by mistake. A send that failed does not count. One invoice can be emailed at most five times in an hour.
The email is refused, and the page says why, when:
- The invoice is a draft or void.
- The invoice has no billing email. Download the PDF and send it yourself, or void the invoice and duplicate it with a billing email.
- The site is a read-only demo.
- The site's email provider cannot send attachments. Download the PDF and attach it to your own email.
While an email of the invoice is being sent, Mark paid, Void and Reopen are refused. Try again when the email is sent. A send that stopped part way stops blocking them after five minutes.
If the page says the email may have been sent, the email provider did not confirm it. Check the Email log before you send it again.
Invoice email is transactional mail. It never goes on the announcement stream and has no unsubscribe link.
Pay by card through a Stripe link#
Staff can make a card payment link for an issued invoice. This needs Stripe: the site's ticketing provider is Stripe, and the operator has set up the Stripe account, mode and key (Stripe ticketing runbook). Without Stripe, Pay by card on the invoice record says so, and the rest of the invoice works as before.
- Open the issued invoice. Find Pay by card on the record.
- Select Make a card payment link.
The link opens a Stripe payment page for the invoice total, in the invoice currency. The page names the invoice number. The server takes the amount, the currency and the name from the issued invoice, never from the browser. The record shows the link and when it stops working. It works for 23 hours, because Stripe ends a payment page after at most a day. After that, select Make a card payment link again for a new one. While a link works, the button is not shown, so the invoice has one working link at a time.
While a link works, the PDF prints it under Pay by card, and Email invoice puts it in the email. The bank details under How to pay still print.
When the sponsor pays, Stripe tells the site. The invoice becomes Paid on its own, with the method Card (Stripe), the Stripe payment ID and the date Stripe took the payment, on the event's clock. The audit log records it. Nobody needs to select Mark paid.
In Stripe test mode, the record says so, and no real money moves.
If Stripe does not answer when you make a link, the page says so. Select Make a card payment link again: the site sends the same request, so Stripe does not make a second page. If Stripe refuses the request, the page says so. Ask an operator to check the Stripe account.
Void and Mark paid close the link. When an operator voids the invoice, or someone records a payment with Mark paid, the link closes on the record, and the site asks Stripe to end the payment page. If Stripe cannot be reached, the record says Stripe was not told, and the page may still work until it ends. The record lists every earlier link and how it ended.
A card payment that does not fit is never applied. Stripe can take a payment the invoice did not expect: after a void, after the invoice was paid another way, through a closed link, for another amount or currency, or through a payment page the invoice has no record of. The invoice does not change. The payment is recorded on the invoice under A card payment needs review, the invoice list shows Needs review, the audit log records it, and the operator gets a system error alert. Check the payment in the Stripe Dashboard, then refund it there or record it another way. Then an operator selects Mark reviewed, writes a Review note and selects Save review. This also closes the system error alert. Staff see the review but cannot close it. While a review is open, no new card payment link can be made. A payment through a page the invoice has no record of also closes every open link on the invoice.
Refunds and disputes stay in the Stripe Dashboard. A refund does not change the invoice. If the money goes back, an operator reopens the invoice.
Mark an invoice paid#
- Open the issued invoice and select Mark paid.
- Set Date paid. It starts at today on the event's clock and cannot be after today.
- Choose the Method: bank transfer, check, card, cash, or other.
- Add a Payment note if you want one, such as a check number.
- Select Record payment.
The status becomes Paid, and the record shows the payment. An open card payment link closes (see Pay by card through a Stripe link).
Reopen a paid invoice#
Operators only. Reopen a paid invoice when the payment did not hold, for example a check that bounced, or when it was marked paid by mistake.
- Open the paid invoice and select Reopen this invoice.
- Enter the Reason for reopening. It is required.
- Select Reopen and the number, such as Reopen INV-0004.
The status goes back to Issued, with the same number, so the invoice can be paid again. The payment is not deleted: it moves to Earlier payments on the record, with your reason. A void invoice cannot be reopened. Reopening a Card (Stripe) payment does not send money back; refund it in the Stripe Dashboard if needed. Staff can make a new card payment link after a reopen.
Void an invoice#
Operators only. Void an issued or paid invoice when it is wrong or no longer due.
- Open the invoice and select Void this invoice.
- Enter the Reason. It is required.
- Select Void and the number, such as Void INV-0004.
The status becomes Void. The invoice stays in the list, and its number stays used: it is never given to another invoice. A paid invoice keeps its payment record. An open card payment link closes. A void cannot be undone. Staff see who can void and reopen instead of the controls.
Correct an issued invoice#
An issued invoice cannot change. To correct one, an operator voids it, then you select Duplicate as a new draft. The copy is a new draft with the same bill-to details, lines, discounts, tax, reference and notes, in the same currency. It has no number and no due date. It links back to the invoice it came from. Check it, save it, and issue it; it takes the next number. You can duplicate any invoice, for example to bill the same sponsor again next year.
Find an invoice#
The list shows the newest invoices first, with the status as a word: Draft, Issued, Paid or Void. Needs review next to the status marks a card payment an operator must check. Use Status to show one status, and Search to find an invoice by its number, the name or email it is billed to, or its reference. Show more invoices continues the same search. One search reads 250 invoices at a time. When those hold no match and older invoices remain, the page says so; select Show older invoices to search on.
Invoice settings#
Open Invoice settings from the Invoices page. The settings hold what every issued invoice copies: The issuer name, address and email, the tax ID, the payment instructions, the number prefix and the fallback currency. Staff can read them. Only an operator can change them, because they hold the bank details. The settings are not part of the public event settings.
The number prefix is 1 to 10 capital letters, digits or hyphens, and starts with a letter. The page shows the next number.
Import records#
Add speakers, sessions and organizations (sponsors and partners) from a CSV file, for example an export from a spreadsheet or another event system. Open Import records under Operations. Staff and operators can use it.
The import saves drafts only. It publishes nothing and sends no email or invitation:
- A new speaker has the status Not invited (draft). It is not in the public speaker directory. Approve or invite the speaker on the Speakers page.
- A new session or organization is a draft, and it is hidden: Show this session when it is published and Show this organization when it is published are cleared. Turn the option on in the editor, then publish.
- A changed session or organization gets a new draft. The published version does not change until you publish.
- A speaker has no draft. A change to a speaker who is not yet approved is saved on the speaker. A change to an approved speaker is not shown on the site: it waits on the Speakers page as Changes pending review, the same as a change the speaker makes. Apply or discard it there. The import does not change an approved speaker's slug. Change it in the speaker editor.
- The import does not change a speaker's status or a record's Show option.
Steps.
- Select the Record type: Speakers, Sessions or Organizations. Import speakers before sessions, so that the sessions can name their speakers.
- Choose the CSV file. The first row names the columns. A file can have up to 500 rows and 5 MB. A cell can have up to 5000 characters. Split a larger file.
- Under Columns, point each field at a column of the file. A column with the template's header, or with the field's name, is selected for you. A field set to Not in this file is never read or changed. Only the columns you map leave your browser.
- Select Preview import. The preview lists each row with its result: New, Update, Matches existing, No change or Error, and the reason for each error. Nothing is saved yet.
- Select Import N speakers as drafts (the words name the type and count). Rows with an error are skipped. The import checks each record again as it saves it, so a field someone edited after your preview keeps their edit, and a record deleted after your preview is not made again. The result lists what happened to each row, and the import writes one row to the audit log.
If the file or the mapping changes after the preview, the import is refused. Preview again.
The external ID. Each row needs an ID from the source system that stays the same each time you export the file, for example a registration number. The import uses it to find the record it made the last time:
- Import the same file again, and nothing changes.
- Change a value in the file and import it again, and that field changes in the draft. Other fields do not change.
- If you edited a field in the admin panel and the file also changes that field, the import keeps your edit. The row shows a note that names the field.
- A field you map for the first time fills only an empty field. A value already in the record is kept, with a note.
- If you deleted a record that an import made, the import does not make it again. To make it again, give the row a new external ID.
- After the import keeps your edit, the file's value counts as imported. If you later undo your edit, importing the same file does not bring the file's value back. Type it in the editor, or change it in the file. The same is true if you discard a speaker change that an import queued.
Records you made before the import. A row that no import has seen before is matched to a record that already exists, so the import does not make a second copy. The row shows Matches existing and names the record. The import links the row's external ID to that record. It fills the record's empty fields and keeps every value the record has, with a note for each field where the file differs.
- A speaker matches by slug: the
slugcell, or the slug the name makes. If the row has an email address and the speaker has a different one, they are different people, and the row is an error. Give one of them another slug. - A session matches by
session_id. With nosession_idcell, it matches the session whose ID the title makes, if that session has the same title and is on the same day. A session with the same title on another day is a different session: the row makes a new one, with a note. - An organization matches by its address (
slug, or the address the name makes). - A record that an import already linked to another external ID is not matched. The row is an error. Check the external ID.
Checks. Each row goes through the same checks as the editor: a speaker's names, email and slug; a session's title, description, day and times; an organization's name, website and order. A row that fails shows the editor's own message.
Download a template with Download the speaker template (or the session or organization template). Each template has the header row below and one example row. You can use other column names and map them by hand.
| Record type | Template header row |
|---|---|
| Speakers | external_id,first_name,last_name,slug,email,organization,job_title,bio |
| Sessions | external_id,title,description,day,start_time,end_time,track,place,location,recording_url,materials_not_needed,speakers,session_id |
| Organizations | external_id,name,slug,tier,website,description,order |
Speaker columns. first_name and last_name are required. If the file has one name column, map it to Full name instead. The first word becomes the first name. slug is the speaker's page address. Leave it empty and the import makes it from the name. A name change does not change the slug; change the slug cell to change it. A new slug that another speaker uses is an error. The import does not change the email address of a speaker who has an open invitation. Change it in the speaker editor.
Session columns. title is required, and a session also needs a description, a day, a start time and an end time before it can be saved. Write each value the way your event settings name it:
day: the day's ID, its date (2026-11-12), or its label.start_time,end_time: 24-hour (13:30) or 12-hour (1:30 PM).track: the track letter or the track name.place: the place ID or the place name.materials_not_needed:yeswhen the session needs no slides or other materials.noor an empty cell means the session needs them.y,n,true,false,1and0also work. Any other value is an error.speakers: speaker external IDs from a speaker import, or speaker slugs, separated by;. A speaker that does not exist, or that was removed, is an error.session_id: the session's address on the site, for new sessions only. Leave it empty and the import makes it from the title. Asession_idthat an existing session uses matches that session.
The import does not set a parent session. Set it in the session editor.
Organization columns. name is required. slug is the page address, for new organizations only. Leave it empty and the import makes it from the name. An address that another organization uses matches that organization (see Records you made before the import). website must start with http:// or https://. order is a number. The import does not set a logo. Choose it in the organization editor.
Remove an imported record the same way as any other record: Delete in its editor or list. The import keeps no copy of the file. For each external ID it keeps only the record it links to and a fingerprint (a digest) of each value it imported, not the values, so a deleted speaker's email address or bio does not stay behind.
Announcements#
The page has two parts: site banners and announcement email.
Site banners#
Create, edit, and remove the ruled message bands shown across the public site. Choose Notice or Urgent, then set the start and end time. You can add one absolute http or https link and its visible label. A save is live immediately. The site shows the announcement only during that window, and a visitor who dismisses it does not see that same record again in that browser.
Announcement email#
Staff and operators can email one announcement to an audience. A banner and an email are separate: Sending an email does not change the site, and saving a banner sends no email.
Under Email an announcement, choose the Audience:
- Registered attendees: Approved attendees and people who claimed a ticket.
- Approved attendees: Accounts with approved attendee access.
- Speakers: Accounts linked to a speaker profile.
A revoked account is never in an audience. Each address gets one copy, even when two accounts share it.
Write the Subject and the Email message in plain text. A blank line starts a new paragraph. You can add one button with Button link (optional) and Button label (optional). The site puts the message inside the event's email header and footer.
Select Preview email. The preview states the recipient count, how many addresses are left out because they unsubscribed, the sender, and where the unsubscribe link goes. It shows the email with links and remote images turned off, and the plain text version. A preview sends nothing.
Select Send a test to me to get one copy at your own address. The subject starts with "Test:". Its unsubscribe link changes nothing.
Select Send to N recipients. A confirmation repeats the count. Select Send to N recipients in the confirmation to send, or Go back. A sent email cannot be recalled.
Any edit after the preview removes the preview, so you confirm only what you saw. If someone registers or unsubscribes between the preview and the confirmation, the server refuses the send and asks you to preview again.
Large audiences. One send can reach at most 5,000 addresses. The site sends 200 addresses at a time and the page keeps going until the send is done, with a progress line. If you close the page part way, the send shows Sending in the history. Select Continue sending there. Nobody gets a second copy: Every recipient has a send-once key.
Saved messages. Save a message you send often, and load it into a new email later. Saved messages are separate from site banners and from the speaker mail wording.
- Save. Write the subject, the message, and the optional button. Enter a Name for this message, at most 80 characters, then select Save as a new message. The name is a label for you. Two saved messages can have the same name; the list shows when each one changed. A saved message keeps the subject, the message, and the button only. It keeps no audience and no address.
- Load. Select Load on a saved message. The subject, the message, and the button go into the form as a new, unsent draft. The audience stays as you set it. Any preview goes away, so you preview again before you send. If your draft has changes you did not save, the page asks first: Replace draft or Keep my draft.
- Update. After a load, Update "name" saves the form over that saved message. Until you select it, your changes do not change the saved message.
- Rename changes the name only. Remove deletes the saved message after you confirm. A draft you loaded from it, and every past send, keep their own copy.
Saved messages stay until you remove them. The site keeps at most 100. Saving, loading, renaming, and removing send no email and do not change the site. A historical demo cannot save, rename, or remove. Each save, rename, and remove writes an admin_logs row with your address. The row does not hold the name or the message.
Email send history lists the sends, newest first, 20 at a time. Select Show older sends for the next 20. Each row shows the subject, the audience, the time on the event's clock, who sent it, and the counts. The list shows no recipient address and no message. The history keeps each send for 90 days, as the email log does. After 90 days the send leaves the history, and the site removes its recipient list and its message. The unsubscribe links in that email still work.
Show details opens one send: the message as the send started it, with a test unsubscribe link in place of each recipient's own link, and the failed addresses with the provider's reasons. It is not a copy of what each person got. Each recipient's copy is in the email log: select Open the recipient messages in the email log. A send from before this link existed has none; its messages are in the email log under the source Announcement. Each time you open details, or select Use this message or Retry, the site writes an admin_logs row with your address and the send. The row does not hold the subject, the message, or any recipient. The details keep the first 50 failed addresses; when there are more, they say so, and the email log has every one. "Unknown result" means the provider did not confirm whether it accepted the message; the site does not resend it. Use this message loads a past send into the form as a new draft. If the email log could not record some messages of a send, the row says so. The counts still come from the email provider, and nobody gets a second copy. A send from scheduled attendee reminders says Scheduled attendee reminder in place of a sender. Only the schedule continues it, so it has no Continue sending. Stopped means the schedule will send no more of it (see Scheduled reminders).
Retry failed addresses. When the email provider is down or busy during a send, the addresses it could not take are kept. The history then offers Retry N failed addresses. It loads the message and limits the next preview and send to those addresses. Preview and confirm as usual. Each send can be retried once, so nobody gets two copies from two retries; if the retry also fails, retry from the retry's row. An address the provider refused for good (for example, an inactive address) and an unknown result are not retried. If a send stopped part way, Continue sending counts an address that failed before the stop as failed again, and offers it for a retry as usual. If it reports recipients "already handled by an earlier call", some of them may not have got the email: the site cannot tell, and it does not resend them. Each recipient's copy is also in the Email log under the source Announcement, and each test under Announcement test.
Unsubscribe. Every announcement carries a one-click unsubscribe link, so a mail program can show its own unsubscribe button. There are two unsubscribe modes, and the broadcast stream's unsubscribe handling picks one:
- Postmark handling (Postmark-managed), the default: Postmark adds the link and the
List-Unsubscribeheaders, and keeps the opt-out on its own list for the stream. The site copies each opt-out from the delivery webhook. - Custom handling, for clients on another email provider (and the console provider in development): the site adds a signed link and the
List-Unsubscribeheaders, and records the opt-out itself.
With both types of handling, the site leaves that address out of every later announcement and out of the rest of a send in progress. Sign-in codes, receipts, and speaker mail still reach the person. To send announcements to that person again, only when they ask, enter the address under Restore an unsubscribed address and select Restore address. With Postmark handling, also reactivate the address on the broadcast stream's suppression list in Postmark; until then, Postmark refuses it and it shows under Failed addresses.
When the composer cannot send. A notice names the reason. The usual ones: The deployment uses the webhook relay, which has no batch send or unsubscribe support yet; the Postmark broadcast stream is not set up (see docs/POSTMARK_PROVISIONING.md, section 2a); or this is the historical demo, which is read-only. You can still preview.
Every send, test send, and restored address writes an admin_logs row with your address. An unsubscribe writes a row without the person's address.
Live updates#
Compose, edit, and delete entries in the live-updates feed shown on the public site. There is no draft/publish step here, unlike the CMS content tabs — a save is live immediately. This is an admin-authored feed only; nothing ingests from Slack or any other external source.
Each entry has these fields:
- Message. The text, at most 1,000 characters. Line breaks stay.
- Category. Optional. One or two words, at most 24 characters, shown as a tag beside the entry. It follows the same rule as a category on the Updates page.
- Who can read it. Everyone, signed in or not; Approved attendees and speakers; or Speakers only. The database enforces this, not only the page: a reader outside the audience cannot load the entry at all. A speaker is an account linked to a speaker record. An attendee whose registration is not approved yet sees only the entries for everyone. On the card, an entry that is not for everyone carries a tag in words: For attendees or For speakers.
- Pinned. Pinned entries show first, above newer entries that are not pinned.
The list shows each entry's audience and category beside its time. Every save writes an admin_logs row with your address, the audience, and the audience the entry had before, so a change that shows an entry to more people can be traced. A delete writes a row too.
Entries from before audiences. An entry saved before the audience field existed has no audience. The database shows it to admins only, so it is gone from the public card. The list marks it Admins only, no audience set, and a notice above the form counts these entries. Select Edit: the form opens on Everyone, signed in or not, which is what the entry meant when you wrote it. Choose the audience and save.
Key dates. The card also lists the event's key dates: the milestones under Settings → Event settings, in Milestones and the registration goal. They are in date order, and two milestones on the same date are in name order. Each shows its name and its date, with the year. The key dates show when the feed is empty too. With no entries and no milestones, the card is not shown. The card shows only while Live updates is on under Features.
Feedback#
Every submission from the public feedback modal, newest first, with a mark-reviewed / archived action. Admins can read the feedback collection directly; only the status change goes through an admin endpoint, because every client write to that collection is denied outright.
Email log#
Every message the site sent, newest first: Sign-in codes, speaker invitations, acceptances and confirmations, feedback receipts, pitch receipts and decision emails, operator alerts, ticket prompts, and announcement email with its test sends. Staff and operators can open it. Nobody can edit or delete a row here. The site keeps each row for 90 days after it sends the message. Automatic maintenance then removes the row, including its recipient address and any stored body.
- Search looks for your text in the recipient address and in the subject. Case does not matter. One search reads the 500 most recent messages. If nothing in those 500 matches, the page says so and offers Search older messages, which reads the next 500.
- Source picks one kind of message. Status picks Sent or Failed, which is what the mail provider answered when the site sent the message. A later delivery report shows as a word in the Status column: Delivered, Bounced, Complained, or Suppressed. Bounced means the recipient's mail server refused the message. Complained means the recipient marked it as spam. Suppressed means the mail provider did not send it, because the address is on the provider's block list.
- One announcement send. The link Open the recipient messages in the email log in the Announcements send history shows only the messages of that send. A search keeps the limit. Select Show every message to drop it. Rows from before this change have no send and do not show under it.
- The filters stay in the page address, so a reload or a shared link keeps them. The search text does not: An address you search for never enters the page address, the browser history, or an error report.
- Preview opens the stored message under its row, with the plain text version under it. The preview runs no script and loads no remote image. A link shows as its words only. So opening a message, or clicking in it, sends nothing to any other site. If the HTML of a message cannot be shown safely, the preview shows the plain text version instead. A stored body stops at 100 KB, and the preview says so when a body was cut.
- Sign-in codes and speaker invitations never store their body or their subject, because each one holds a code or a link that signs a person in. Their rows show "Not stored", and their preview says why.
- Every preview you open is recorded in the admin log with your account and the message's record. The record never holds the address or the subject.
Sent messages are closed to every browser, admins included. The page reads them through two admin-gated endpoints.
Change requests#
Requests for a change to the site, from signed-in visitors and from staff. Both kinds arrive in one list, newest first. Staff and operators can open it.
- The feature is off by default. An operator turns on
changeRequestsin Settings → Features. While it is off, the footer has no Request a change button, this page has no form, and the server refuses every request. The list, the status changes, and Remove still work, so you can clear the list after the feature is off. - Only a signed-in visitor can send one. The footer shows Request a change only to a visitor who is signed in, and the server refuses a request from an address that is not verified. The request is stored with the sender's sign-in address. One account can send 5 requests in 15 minutes. The server refuses more, and the refusal says how many minutes to wait.
- Send one yourself from the Request a change panel at the top of the page. It goes through the same server checks and the same limit.
- Show picks the requests the list shows: Open (new and in progress, the default), All, New, In progress, Done, or Declined. The choice stays in the page address.
- Each row shows the status as a word, the time the request arrived, the sender's address, the page it is about, and the text. The row's button is the next step: Mark in progress, Mark done, or Reopen. Decline closes a new or in-progress request.
- Remove deletes the request and its text. It cannot be undone.
- The admin log records every request, status change, and removal, with the account that did it and the request's record. The log never holds the text of a request.
When an account is deleted in Attendees, the change requests it sent are deleted too, and the server refuses a new request from that account's open session.
What the sender sees#
The sender follows their own requests on Your change requests (/profile/change-requests). The footer links to it while the feature is on, and so does the dialog after a send. After the feature is off, the footer keeps the link for a signed-in reader who sent a request from that browser. Every email to the sender links to the page too. The page shows only the sender's own requests. For each request it shows the text and page they sent, the status, and the history of status changes with their times. It does not show who made a change.
When a request is Done or Declined, the page also shows the reply and the result link you write for the sender. It shows nothing else that staff see: no staff address, no email record, and no other sender's request. The page works while the feature is off, so a sender can still read what they sent.
Reply and result link#
A done or declined row has Reply and result link (or Reply for a declined request). Write the reply in plain words. The sender reads it on their page. It is not emailed. If the request's outcome changes while the form is open, for example when someone reopens it, the save is refused. Read the request again, then write the reply.
The result link is optional. It must be a page on this site, and you must open it and select I opened this link and it shows the change before you save it. Mark a request done only when you checked the change yourself. A deploy or a publish that ran is not proof that the change is live. Nothing in this system marks a request done by itself, and nothing acts on the text of a request.
Emails to the sender#
Two emails can go to the sender. Both are off by default. An operator turns them on under Manage requester emails at the top of the page. Every staff member sees a line that says which emails are on.
- The acknowledgement goes when a request is stored.
- The outcome email goes the first time a request is ever marked Done, and the first time it is ever marked Declined. The server reads the request's history, so turning the email on later sends nothing about an outcome a request already had. Marking a request done again, or reopening it and marking it done again, sends nothing more.
The wording is fixed in the code, and no editor changes it. An email never quotes the request, the page, the reply, or the link. It says what happened and links to the sender's own page. The emails go on the transactional stream. An address that unsubscribed from announcements gets none of them. A historical demo sends none.
Each row lists the emails of that request and their state: Sent, Not sent, May have been sent, Sending, or Not sent: unsubscribed. A failed email never undoes the request or the status. Retry the … email sends it again with a new attempt. For an email that may have been sent, check the Email log first and confirm, because a retry can deliver a second copy. A retry is refused when that email is off, or when the request's status changed since.
Response targets#
The system does not promise a reply time, and no email names one. Decide your own targets as policy, for example "acknowledge within two working days, and record an outcome within ten". Write them where your team and your visitors can read them, such as a page on the site. Use the Open filter to find requests that wait for a reply.
Materials#
Session materials review — upload or link files against a session, with an optional embargo that holds the material until the session ends. One file can be at most 9 MiB. Prefer a real label ("Slides") over a bare URL as the link text a viewer sees. This collection is fully server-only, even for an admin's direct read, so every action here goes through Cloud Functions. Staff and operators both use this page.
The table. One table lists the materials for every session, hidden sessions included. It shows at most 2,000 materials. Past that, a notice above the table says that some materials are not shown. Each row gives the file name, the word File or Link with its storage path or address, the session, the review state as a word, and when the material last changed. Select Material, Session, or Changed at the top of a column to sort by it; the button states the order in words, such as A to Z or Newest first. The table opens in schedule order. Under Show, the Session and Review filters narrow the rows. Add a link appears when one session is chosen. Refresh reads the list again and says Refreshing… until the list arrives. If the list does not load, the table stays as it was and the page says so.
Download one file. Select Download on a file row. A link row has Open link instead, which opens the address in a new tab. A stored address that is not a web address gets no link, and the row says so.
Download an archive. Tick the files you want, or tick the box at the top of the column to select every file row shown, then select Download as archive. The browser saves session-materials.zip, with one folder per session (named by the session id) and each file under its own name. Two files with the same name in one folder are saved as, for example, slides.pdf and slides (2).pdf. Two sessions whose ids give the same folder name get a folder each, the second as, for example, talk-one (2). Links have no checkbox and never go in an archive. One archive holds at most 50 files and 9 MB, because the server sends it as one stream and the hosting platform stops a stream at 10 MB; for more, download in parts. The line above the table counts what is selected. A change to either filter clears the selection. The server builds the archive and sends it straight to your browser: There is no shareable download link, and nothing is stored. If the transfer stops part way, the browser saves nothing and the page says so.
Every archive is recorded. The server writes one admin log entry per file in the archive, with your address, before it sends the first byte. If those entries cannot be written, the server refuses the archive. A single-file download writes no entry.
Coverage. The Coverage panel names the sessions with no materials and the speakers with no materials on any of their sessions. A pending or approved material counts; a rejected one does not. A session marked as needing no slides or other materials is left out. A session counts only when its published record names at least one speaker who is not removed, so a session with no speaker is left out. A speaker added in an unpublished draft does not count until the session is published. The panel reads the same list as the table, before any filter, and states when it read it. When the list passes 2,000 materials, the panel says so and shows no figures. The panel also counts hidden sessions and speakers who have not accepted an invitation. Speaker reminders counts neither, so a speaker the panel names can be absent from Materials missing there.
Version history#
Every publish keeps a version of the record. A version holds the record's content as it went live, the time it was published, and the account that published it. Saving a draft adds no version. Staff and operators can open it.
- Pick a collection, then a record. Collection picks content blocks, pages, sessions, organizations, updates, or the timeline. The list shows the records that collection holds now, with their state, the live version, and the time of the last publish. A record whose live version is hidden says Hidden, and the site shows nothing for it. Search by name or id narrows the list as you type. Order records by sorts by the most recent publish or by name. The collection, the search, and the order stay in the page address, so a reload or a shared link keeps them.
- Read a version. A record's page lists its versions, newest first, 20 at a time. Load older versions reads the next 20. Each version names its number, its time on the event's clock, and the account that published it. Its table lists each field that changed, with the value before and after the publish. The first version lists its values. A long value shows its first 300 characters, and Show all opens the rest. Every value shows as plain text: Rich text shows its tags, and a link is not clickable.
- A deleted record keeps its versions. The list does not show it, because the list shows the records that exist now. Its page still opens at
/admin/versions/<collection>/<id>. - Versions cannot be changed or restored. The page only reads the history. To go back to an earlier version, copy its values into the record's editor, then save and publish. A value published by mistake stays in the history. An operator can remove it in the database console.
System errors#
Unresolved system-error rows — the operational surface for things like an invalid email-template override falling back to its code default. Each row has a resolve action. If you are chasing down why a template edit "is not working", check here before assuming it is a bug: A rejected override logs here and silently keeps serving the last-good copy rather than breaking the send.
Data export#
The event's records as spreadsheets, and the event data package for the handoff after an event. Open it from the Operations group on the rail. Every export writes an admin log entry before the file is sent; if the entry cannot be written, no file is made. The server keeps no copy of any file.
Spreadsheets. Each row saves one CSV file named for its list and the date, such as sessions-2026-10-01.csv. The file shows the current version of every record and its state (Draft, Live, or Live with unpublished changes), so a draft is in the file too. The sessions and organizations files also have a Hidden column: yes for a record the site does not show. A cell that starts with =, +, -, or @ gains a leading apostrophe, as in the attendee export, so a spreadsheet shows it as text.
| Spreadsheet | Tier | Columns |
|---|---|---|
| Sessions | Staff | Session ID, title, state, hidden, day, date, start and end time, time zone, type, track, location, speakers, speaker IDs, the session it is part of, description, number of materials, no materials needed (yes or no) |
| Speakers | Staff | Speaker ID, first and last name, email, organization, role, status, published sessions (live and not hidden), social handles, bio, headshot path in Storage |
| Organizations and sponsors | Staff | Organization ID, name, state, hidden, tier, website, description, support description, logo path, order |
| Ticket orders | Operator | Order ID, created, mode (live or test), source, status, payment status, purchaser email, currency, subtotal, discount, tax, total, refund status, refunded, tickets, ticket IDs |
| Admissions | Operator | Ticket ID, order ID, provider, ticket type, holder's first and last name, email, status, whether an account claimed it, checked in, purchased |
| Invoices | Staff | Invoice ID, number, status, bill-to name, contact name, billing email, organization ID, reference, issue date, due date, currency, subtotal, discounts, tax, total, paid on, payment method, payment note, void reason, the invoice it was copied from, created. A reopened invoice's earlier payments are in the event data package. A payment through the Stripe pay link shows the method as Card (Stripe). |
No spreadsheet holds an account id, an invite token, staged speaker edits, a private note, a checkout link, bank details, or a raw payment provider record. Amounts are in the currency's major unit (25.00). Staff do not see the two operator rows; the page says who exports them. Attendees and session pitches export from their own pages, because those exports follow the page's filter.
The event data package. Operators only. Download event data package saves one zip file named event-data-package-<date>.zip. It holds a JSON file for each record type, CSV files for the main lists, manifest.json with record counts and checksums, and README.md, which describes each file, the links between files, and how to handle the personal data in it. Choose the parts under Parts to include; all nine are chosen at the start. Include staff notes on session pitches is off at the start. The package holds live records only, in its CSV files too: a draft that was never published, or a newer draft of a live record, is not in it. A hidden live record is in it, with visible: false in JSON and Hidden yes in CSV. The package never holds the admin allowlist, credentials, the admin log, or raw provider records. It holds file paths and links for media and materials, not the files. The Invoices part holds every invoice, drafts too, because an invoice draft is a record and not an unpublished edit. Each issued invoice keeps its own copy of the issuer block and payment instructions. Its card payment links and any card payment flagged for review go too, with their status, amount and times. Like ticket orders, they hold no Stripe ID and no payment page address; the Stripe Dashboard is the record of those. The invoice settings and the number counter stay behind.
The page sends a package of up to 9 MB built from at most 50,000 stored documents. Each document counts once, even when two files draw on it. A larger event gets a message that names scripts/export-event-package.cjs, which builds the same package from a terminal. Archive and hand off a past event covers the script, how to check a package, a full Firebase backup, the Storage files, and how to keep the past site online read-only.
Settings → Event settings#
The event's own identity fields: Name, dates, time zone, venue, sender address, social accounts, milestones, and the rest. The venue's places, movements, and map are set here too, in the panels described below. This is a merge-then-validate write — the form only sends the keys it is actually changing, and fields it does not touch (the legal postal address, the SEO metadata) are left alone.
The time zone is chosen from a list. Type part of a city, a region, the zone's usual name, or an offset into Time zone, such as Chicago, Eastern Time, or UTC+05:30. Each match shows the city, the zone's name, and its offset today. Use the arrow keys and Enter, or select one. Escape, or leaving the field without a choice, keeps the saved zone. The saved zone is marked Current. The site still stores the IANA name, such as America/Chicago, and the field states it under the search. A line under the field gives the UTC offset on your event days. If the clocks change during the event, it names both offsets and the day the second one starts.
Days. Select + Add day to add a day, then fill in its label, date, and start and end times. List the days in date order. You do not type a day id: a new day gets one, such as day-3, that no other day and no session uses, and a saved day keeps its id. Sessions store that id, so changing a day's label or date never moves its sessions. Each day has its own Remove control. If sessions are on the day, the day says so, and Remove first explains what happens: the sessions stay saved but leave the public schedule until you give each one another day. Choose Remove day … anyway or Keep day …. Nothing is removed until you save.
Tracks live here too: The lines your event runs when sessions happen at the same time in different rooms. Each track has a letter (A to Z) and a name, and the schedule shows both — a reader tells two lines apart by the letter and the name, never by colour alone. Sessions point at a track by its letter, so renaming a track is one edit here rather than a change to every session. Leave the list empty if everything happens in one room. + Add track starts a new track on the first letter that no track has and no session uses. Each track has its own Remove control. If sessions use the track, Remove first explains that they lose their track and run across the whole schedule until you give them another one. Changing a used track's letter shows the same warning under the letter field. Choosing a letter that sessions already use, from a track removed earlier, says that those sessions join this track when you save. Add and Remove wait until the page has read every live and draft session; until then, each panel says it is checking.
What the track list changes on the public site. Once you list tracks, the schedule draws as a grid on a wide screen: Time down the left, one column per track, in the order you listed them. On a phone — and for anyone reading with a screen reader — the same day is a time-ordered list. It contains the same sessions in the same order as the wide-screen grid. A session with no track runs across the whole width, which is what a plenary is. A session that names a parent is listed under it as a stop on the way through it rather than as a separate entry, in both views.
The register control. For external registration providers, the external registration URL and register button label configure the action in the home lead and site header. It opens the provider's HTTPS page in a new tab; the default label is "Register". Leave the URL empty when there is no external destination. These providers read the same field for their registration emails.
When the deployment's public catalog identifies Stripe and an operator has published the catalog, both controls say Get tickets and open /tickets on this site, including when no external URL is configured. The native page shows the current offer or explains why sales are closed. Until the catalog is published, neither control shows, and the external URL is not used. The server also hides the action, with the same answer as an unpublished catalog, when the Stripe provider settings are incomplete or the event configuration cannot be read. A published catalog keeps Get tickets while new sales are paused or every sales window is closed, so a visitor can read when sales open. If the lookup request itself fails, for example during a network outage, both controls keep Get tickets; the page then offers a retry. The header checks again on each page change and when the browser tab becomes visible, at most every 30 seconds. A manual, Eventbrite, or none provider retains the configured external action. Historical demos keep their existing external behavior and do not probe live ticketing. Set the deployment provider through the operator workflow; editing an external URL does not switch the payment provider.
Past days become back issues. When a day ends, its page keeps every word and quietens: The colour drops out, the day head says "Back issue", and the controls that act on a live event — bookmarking, reactions, adding to a calendar — go away. Nothing is hidden, and every link still works. Setting an archive date for the whole event does the same thing to every day at once.
The home page counts down on its own. Before the first day starts, the home page shows a live countdown to that moment, reading the dates and timezone set here. Once the first day begins, the countdown stops and the page states that the event is running, checking about once a minute so it moves on to stating the event has ended on its own once the last day is over — a reader does not have to reload the page for that. Setting the archive date states the same "event has ended" line; nothing looks different to a reader. Nothing to configure: the page always reads the same dates, timezone, and archive date this section sets.
Printing. Print any schedule page and you get the handout: Every day of the event, every session and every stop under it, tracks named by letter and name, no buttons. You do not have to prepare anything for it, and you get the light edition even if you print from a dark screen.
Places#
A place is one named room or hall at the venue. Sessions, movements, and the venue map all point at places, so each room's name is stored once. Rename a room in its place, and the transfer lines, the room list beside the venue map, and the admin sessions list all follow.
Select Add place in the Places panel. Each place has three fields:
- Name: The name readers see, such as Main hall. Required.
- Id: The stable key that sessions, movements, and map markers store. Use lowercase letters, digits, and single hyphens, such as
main-hall. For a new place, the id fills in from the name as you type. Type in the id field and the id stops following the name. Until you save, a movement or map marker that already picked the new place follows its id when the id changes. - Floor: Optional, such as Level 2. The room list beside the venue map and the transfer line both show it.
Keep a saved id the same. Change a place's name as often as you need to; the id stays. A saved place's id does not follow its name. If you change a saved id anyway, every movement and marker that named the old id is refused until you point it at the new one. The server also refuses the save while a live or draft session still uses the old id.
Removing a place. Select the Remove control on the place's row. The place's movements and its map marker go with it, and a line above the Places panel says what will go. Nothing is removed until you save. You cannot remove a place that a live or draft session uses. Its remove control is off, and the row names the sessions that use it. Move those sessions to another place first. The server makes the same check when you save.
How a session uses a place#
In the session editor, Recorded place picks one of your places, or No recorded place. It is a separate field from Public location text. The location text is the wording attendees read, and it does not change the recorded place. The site never matches the location text against place names. The sessions list in the admin shows the recorded place's name, or the location text when a session has no recorded place.
A session can only name a place that exists. The server refuses any other id, and refuses every id while the venue has no places at all. Add the place in Event settings first, then pick it on the session.
Movements#
A movement is one recorded walk from one place to another. The site shows a walk only where a movement records that exact pair of places. It never guesses a walking time.
Select Add movement in the Movements panel and fill in:
- From and To: Two different places from your list.
- Walking minutes: A whole number from 0 to 120. Use 0 for rooms across the corridor; the site then says "under a minute’s walk".
- Accessible route: Optional. The step-free way between the same two places, in your own words. Leave it empty if nobody has checked one. The site then says nothing about a step-free route, rather than saying there is none.
A movement is one way. A walk from the main hall to the studio says nothing about the walk back, so record the reverse as its own movement, even when it takes the same time. The site does not join movements either: A walk from A to B and a walk from B to C do not make a walk from A to C.
Where a walk shows. A transfer line appears in two places only:
- On My schedule, between two sessions a reader bookmarked, one after the other on the same day, when a movement records the walk from the first session's place to the second session's place.
- In a session's calling points, when a child session sits in a different place from its parent and a movement records that walk.
The line names both places, the destination's floor, and the walk. When you recorded an accessible route, a second line gives it after "Step-free route:". The full schedule shows no transfer lines, because a reader scanning the programme is not walking it in order.
Removing a movement. Select Remove movement on its row. It goes when you save.
Venue map#
Upload a map of the building and the travel page prints it, with the venue's rooms listed beside it. Under Venue map you choose or upload the image the same way you pick any other picture, and then write the alt text: One sentence saying what the map shows. The alt text is required — a map with none does not publish, because an image nobody described tells a reader using a screen reader nothing at all. The picture has to come from the media library, so a link to a map on somebody else's site is refused; upload the file instead.
The map lists your places. The room list beside the map is your Places list, in the same order, with each room's floor. Every place is listed, marked or not, so a reader who cannot see the picture still gets every room name. Add your places before you mark them: A marker can only name a place that already exists. The Map URL field in the Venue panel is a different thing. It links to an outside street map, and it does not use places.
Where the map appears. The travel page carries a section called Venue map (its id is travel_map), and the map draws wherever that section sits, so you move the map by moving the section in Pages. If your travel page does not have that section — every site set up before this feature shipped is in that position — the map draws at the end of the page instead, so uploading one always publishes something.
Marking rooms on the map. A marker puts a numbered dot on the image where one of your places is. Add one with Add marker, pick the room, and type how far across and down it sits as percentages of the picture: 0 across is the left edge, 100 the right; 0 down is the top, 100 the bottom. The number on the dot is the number in the list, so a reader can match the two. Numbers run down the list in the order your places are listed, not the order you added the markers. One marker per room. Remove marker takes one off, and removing a place takes its marker with it — the panel says so before you save.
Taking the map down. Clear the map image. The alt text and the markers are cleared with it, and the travel page shows no map after you save.
Rules for places, movements, and the map#
The places and movements fields are checked as you type, and a field that breaks a rule is marked at once. The map fields are checked when you select Save event settings. Either way, Save event settings stays available. A save with a marked field sends nothing and moves focus to the first field that needs a fix. The server checks the same rules again and names the field it refused.
| Field | Rule |
|---|---|
| Place name | Required. |
| Place id | Lowercase letters, digits, and single hyphens. No two places share an id. |
| Removing a place | Refused while a live or draft session uses it. |
| Movement From and To | Two different places from your list. One movement per direction for each pair. |
| Walking minutes | A whole number from 0 to 120. |
| Map image | Chosen from the media library, so its path starts with cms-images/ or branding/. |
| Map alt text | Required once an image is chosen. |
| Marker room | One of your places. One marker per place. |
| Marker across and down | A number from 0 to 100. |
Social accounts#
The Social accounts panel holds the event's own accounts. The site footer and the footer of every built-in email list them, in the order the panel shows. In an email, the formatted copy links each service name, and the plain-text copy gives one line per account: The service, then the address. An email template override that replaces a body keeps the footer that body has. Leave the list empty and neither footer shows social links.
Select Add account. Focus moves to the new row's Service field. Each account has three fields:
- Service: The name readers see, such as Mastodon. Required, at most 40 characters.
- Handle: Optional, such as @eventname, at most 40 characters. The site footer prints it beside the service, so two accounts on one service can be told apart. The email footer leaves it out.
- Link: The full address of the account. It must start with
https://orhttp://.
Removing an account. Select Remove account on its row. Focus moves to the remove control of the account that takes its place, or of the one before it when you removed the last, or to Add account when none is left. The account goes when you save.
What the save checks. The accounts are checked when you select Save event settings. A save is refused, and focus moves to the first field that needs a fix, when a service name is empty, a link is not a full https:// or http:// address, or the same service and link are listed twice. A link with no scheme, such as example.org/@eventname, is refused, and so is a javascript: link. The server checks the same rules and names the field it refused.
Social hashtag. One word with no spaces, such as #EventName. The event settings store it, but the site and its email do not show it.
Milestones and the registration goal#
The Milestones panel lists the dates the Overview counts down to, such as the day proposals close or the day the programme is announced. The Overview shows them in date order, each with its date and the days left: In 12 days, Today, or 3 days ago once it has passed.
Anyone can read the milestones and the goal. They are stored with the event settings, which the public site reads, so every name, date, and goal is public. Keep private notes out of them. While Live updates is on under Features, the live updates card shows the milestones to visitors as Key dates.
Select Add milestone. Focus moves to the new row's name field. Each milestone has a name, at most 80 characters, and a date. An event can list 20 milestones; at 20, Add milestone does nothing and says so until you remove one. Remove milestone moves focus to the milestone that takes its place, or to the one before it when you removed the last, or to Add milestone when none is left. A milestone goes when you save. A milestone with no name or no date is refused when you save, and focus moves to the field that needs it.
Registration goal is in the Registration panel: The number of approved attendees you are aiming for. Anyone can read it. The Overview sets the approved count against it, as a sentence and a bar. Leave it empty for no goal. It must be a whole number from 1 to 1,000,000; the server refuses anything else and names the field.
With no milestones and no goal, the Overview shows no milestone panel at all.
Settings → Features#
Feature flags, wired to a whole-document replace — every known flag is always sent, and an omitted flag means disabled. When a new flag is added to the platform it appears here automatically, because the form's key list comes from the same shared schema the server validates against.
changeRequests turns on the change request form in the footer and on the Change requests page. It is off by default. See Change requests.
Settings → Badges#
The badge catalog: Categories, each with a max-picks cap and its list of badges, which attendees self-select from on their profile. Also a whole-document replace: editing one badge sends the complete set of categories back and preserves the configured custom-badge block list.
Custom badges. The separate feature is off by default. When enabled, attendees can enter up to three badges of 24 characters each. Reserved role words are always blocked; config/badges.customBadgeBlockList adds event-specific words. The owner form and public projection use the same validator. The category editor preserves this list; an operator changes it through the badge configuration API.
Remove a custom badge. In Attendees, select the removal action beside the badge and confirm. The result stays on the page and an admin_logs entry records the action. Turning the feature off removes custom badges from the public projection; attendees can still edit their other profile fields.
Settings → Access#
Operators only. One ruled table of every admin account with its tier word, and a form to grant access to a new address.
Grant access. Enter the address and pick a tier. The page asks you to confirm, states what the person gains, and only then writes the grant. The address is stored lowercase, so the case you type does not matter. The person signs in with that address the way every admin does, by Google or by the emailed code; there is no invitation to send.
Change a tier or remove access. Each row carries two quiet actions: Change to staff or Change to operator, and Remove access. Each one opens a confirmation under the grant form, above the table, that names the account and the consequence. Removal sits on the alarm ground; a tier change does not, because it narrows or widens access without deleting anything. Cancel returns you to the control you pressed.
Set an end time for staff access. When the tier is staff, the form shows Access ends (optional). Pick a date and time in your own time zone, at most 90 days from now. Leave it empty for access with no end time. The confirmation states the end time. It also says that the account can edit and publish content until then, because an end time does not make the account read-only. Operator access has no end time.
When the end time comes. The access stops at that moment. The server and the database rules check the end time on every request. A sign-in that started before the end time does not keep access after it. Within a minute, a scheduled job takes the account off the access list and records this in the admin log. An open Access page asks the server again just after an end time, so the row shows Ended without a reload. Until the job runs, the row shows Ended and the time it ended. After that, the account is gone from the table. To give it access again, grant it again. An end time that cannot be read counts as ended: the row says No access, and the server and the rules refuse the account. The job leaves such a row in place, so you can decide what to do with it.
Change or remove an end time. Enter the address again with the staff tier. Pick a new end time to move it, or leave the field empty to make the access permanent. Change to operator removes the end time. Remove access removes it too.
An open browser tab. The server checks the end time on every request. A browser tab that was open before the end time may still read admin pages through its open database connection. It cannot save a change, because every change goes through the server. The scheduled job ends this within a minute: it changes the access list, and every connection is checked against the new list. To stop an account at once, remove its access.
At least one operator stays. The server refuses a change that would leave the deployment with no operator: Demoting or removing the last operator, your own grant included. Grant a second operator first. The refusal is stated in place, in the server's words.
What is recorded. Every grant, change, and removal writes a row to the admin log with your address, the account, and the tier it moved from and to. When an end time is set, moved, or removed, the row also holds the old and the new end time. When the scheduled job removes an ended account, it writes one row for that account, with "system" as the actor and the end time. A change that changes nothing writes no row. Only operators can read the access lists, the end times, and the admin log.
When it takes effect. At once. The database rules, the server, and the admin rail all read the access lists live: A granted account is admitted on its next request, a removed account is refused on its next request, and the rail shows the right sections the next time the person signs in or reloads.
Legal pages#
Privacy policy and terms of service ship as CMS content pages seeded from provider-aware templates, each clause needing review flagged [Client legal review required]. They stay flagged — a persistent banner in the admin panel and a visible notice on the public page — until an admin clears the review flag after the client's counsel has actually reviewed the seeded copy. See CLIENT_ONBOARDING.md's legal review section for the full context. Do not publish another organization's terms verbatim; the seeded text is a starting point, not a finished policy.