Openfront Real Estate

Listings, leads, showings, offers, deals, closing records, and commissions for a brokerage, with a public listing site.

Source · Live site · Catalog

Openfront Real Estate is a self-hosted app for one brokerage. Visitors browse listings, ask questions, request tours, and save homes and searches. Brokerage staff manage listings, leads, showings, deals, closing records, commissions, and team setup in the dashboard.

To install it, see Getting started.

Escrow, title, mortgage, commission, and syndication records hold what staff enter and track.

Concepts

ConceptMeaning
BrokerageThe tenant. The storefront serves the primary one
MembershipA user's seat: owner, admin, team_lead, agent, coordinator, analyst
PropertyA listing, sale or rent
Authority recordsLicense, listing engagement, publication, and representation records that gate publishing and actions
LeadA person with an interest, deduplicated per listing by an email or phone identity key
DealA transaction with offers, milestones, documents, and closing records
LedgerAppend-only commission entries

Tenancy

  • Brokerage is the tenant. Operational records have a required or derived brokerage relation. Child records such as media, inspections, and milestones take their brokerage from the parent.
  • Access comes from activeBrokerageIds(session): the user's selected activeBrokerage, or their one live membership. A user with two live memberships and no selection sees nothing.
  • BrokerageMembership links a user to a brokerage with a role (owner, admin, team_lead, agent, coordinator, analyst) and a status (invited, active, suspended).
  • Role flags decide what a member can do: canAccessDashboard, canManageListings, canManageLeads, canManageShowings, canManageDeals, canManagePeople, canManageRoles, canViewReports, canManageOnboarding, canSeeOtherPeople, canEditOtherPeople. Without canManagePeople, agents work only on records assigned to them.
  • Storefront customers are User rows with an activeBrokerage and no membership. Their showings, inquiries, saved homes, and saved searches are linked to their account by the server, never by matching an email.
  • The storefront serves the first active brokerage marked isPrimary.

Data model

The model registry has 44 lists.

AreaLists
Brokerage and peopleBrokerage, BrokerageSettings, BrokerageMembership, User, Role, AgentTeam, AgentTask
ListingsProperty, PropertyMedia, OpenHouse, ListingSyndication, MarketComp, PropertyValuation, Neighborhood, SourceFeed
Authority recordsListingEngagementAuthority, ListingPublicationAuthority, PractitionerLicenseAuthority, RepresentationAuthority
Leads and showingsLead, LeadActivity, LeadRoutingRule, Showing, ShowingFeedback
Shopper dataSavedProperty, SavedSearch, SavedSearchAlert
Deals and closingDeal, Offer, TransactionMilestone, DealDocument, DocumentEvidenceEvent, Disclosure, PropertyInspection, Escrow, TitleOrder, MortgageApplication
CommissionCommission, CommissionLedgerEntry
Records and eventsRetentionPolicy, TransactionFileEvent, DomainOutboxEvent, DomainAuditEvent

Neighborhood has no brokerage field. It is shared reference data and is managed through the generic list page.

Main state machines

RecordStatesChanged by
Propertydraft → coming_soon → active ⇄ pending → sold or rented; any → off_markettransitionPropertyStatus
Leadnew → attempted_contact → qualified → tour_scheduled → offer → closed or losttransitionLeadStatus
Showingrequested → scheduled → completed or no_show; requested or scheduled → cancelledtransitionShowingStatus, reviewShowingCommand, completeShowingWithFeedback, cancelCustomerShowing
Dealprospecting → active_search → offer_drafted → under_contract → inspection / financing → closed_won; open → closed_losttransitionDealStage
Offerdraft → submitted → countered, accepted, rejected, or withdrawntransitionOfferStatus, createCounterOfferCommand
Milestonenot_started → in_progress → complete; blockedtransitionTransactionMilestoneStatus
Documentdraft → sent → signed → archivedtransitionDocumentCommand
Disclosuredraft → sent → acknowledged; expiredtransitionDisclosureStatus
Inspectionscheduled → completed, needs_follow_up, or waivedtransitionInspectionCommand
Commissionprojected → approved → paid; clawed_backtransitionCommissionStatus
Open housescheduled → completed or cancelledtransitionOpenHouseStatus
Agent taskopen → in_progress → done; blockedtransitionAgentTaskCommand
Outbox eventpending, processing, dispatched, failedreplayDomainOutboxEvent resets failed or dispatched to pending

Authority records, DocumentEvidenceEvent, CommissionLedgerEntry, RetentionPolicy, TransactionFileEvent, and DomainAuditEvent are append-only.

Workflows

Publish a listing. Create a draft Property and add media and open houses. A listing shows on the storefront only when it is active or coming_soon and there is an effective practitioner license, a signed listing engagement with marketing authority, and public_site publication authority for it.

Capture a lead. The storefront question form (submitListingInquiry) and tour form (requestPropertyShowing) create or reuse a Lead. Leads are deduplicated per listing by an email or phone identity key. The first matching active LeadRoutingRule picks the agent, then the listing agent, then a fallback rule. The agent must still hold an active membership; otherwise the lead stays unassigned. Staff can also add leads by hand.

Tour a home. A tour request creates a requested showing. Its length comes from showingDurationMinutes in brokerage settings, clamped to 15–240 minutes. Staff confirm it, complete it with feedback, mark it no-show, or cancel it. Confirming checks that the property and agent have no other confirmed showing at that time.

Close a deal. Create a deal from a lead and listing. Add offers and counters, checklist milestones, documents with evidence events, disclosures, inspections, a mortgage application, a title order, and escrow. Move the deal stage to closed_won or closed_lost.

Track commission. Create a commission per side. Use the ledger commands to propose and approve a split, record a receipt, reverse an entry, and reconcile.

Run the team. Owners and admins invite and suspend members. Only an owner can add another admin. Staff manage teams, tasks, and lead routing rules.

Keep records. Record retention policies, apply retention to a deal file or document, place or release a legal hold, and create an export manifest.

Named GraphQL operations

All custom operations are in features/keystone/mutations/index.ts.

Public and customer queries: publicBrokerageIdentity, publicRealEstateListings, publicRealEstateListing, customerShowingHistory, customerInquiryHistory, customerSavedProperties, customerSavedHomes, redirectToInit.

Public and customer mutations: submitListingInquiry, requestPropertyShowing, registerRealEstateCustomer, cancelCustomerShowing, savePropertyForCurrentUser, removeSavedPropertyForCurrentUser, saveSearchForCurrentUser, deleteSavedSearchForCurrentUser.

Operator queries: platformContacts, platformLeadDetail, platformShowingRequestViewer, platformListings, platformShowings, platformShowingDetail, scheduledShowingReceipt, platformPipeline, platformPeople, platformReports, platformOperations, platformStorefrontSettings, domainOutboxEventStatus.

Status transitions: transitionLeadStatus, transitionPropertyStatus, transitionShowingStatus, reviewShowingCommand, transitionOpenHouseStatus, transitionOfferStatus, transitionDealStage, transitionCommissionStatus, transitionDisclosureStatus, transitionTransactionMilestoneStatus. They take an idempotency key and can check the expected status or updatedAt.

Operator commands:

  • Leads and showings: addLeadActivityCommand, resolveDuplicateLeadCommand, scheduleShowingForLeadCommand, completeShowingWithFeedback.
  • Deals: createOfferCommand, createCounterOfferCommand, createChecklistItemCommand, transitionDocumentCommand, transitionInspectionCommand, updateDealFinancialTermsCommand, updateDealNextStepCommand.
  • Authority and evidence: recordPractitionerLicenseAuthorityCommand, recordListingEngagementAuthorityCommand, recordListingPublicationAuthorityCommand, recordRepresentationAuthorityCommand, appendDocumentEvidenceEventCommand.
  • Commission ledger: proposeCommissionAllocationCommand, approveCommissionAllocationCommand, recordCommissionReceiptCommand, reverseCommissionLedgerEntryCommand, reconcileCommissionLedgerCommand.
  • Records: recordRetentionPolicyCommand, assignTransactionFileRetentionCommand, recordTransactionLegalHoldCommand, createTransactionExportManifestCommand.
  • Feeds and alerts: queueSourceImportCommand, transitionListingSyndicationCommand, queueSavedSearchAlertCommand, updateSavedSearchAlertCommand, replayDomainOutboxEvent.
  • People and tasks: inviteBrokerageMemberCommand, suspendBrokerageMemberCommand, transitionAgentTaskCommand.
  • Setup: runRealEstateOnboarding, dismissRealEstateOnboarding, updatePlatformStorefrontSettings.

Some dashboard pages also use the generated Keystone CRUD mutations, for example to create listings, leads, media, open houses, deals, tasks, routing rules, inspections, escrow, title orders, mortgage applications, and commissions. Those writes still go through list access rules and tenant hooks.

Onboarding

The first user is created at /dashboard/init together with a starter brokerage, an Admin role, and an owner membership. The dashboard then offers a basic or complete setup through runRealEstateOnboarding. Details are in Getting started.

Integrations

  • Email: password reset email through nodemailer and the SMTP_* variables. No other email is sent.
  • AI chat: an optional dashboard chat at /api/completion uses OpenRouter. It is available only to signed-in users with dashboard access and calls the MCP endpoint at /api/mcp-transport/[transport] with the user's session cookie.
  • MCP endpoint: exposes generic tools that list models, query, search, create, update, and delete data through GraphQL with the caller's session. The same file still declares e-commerce tools (products, carts, checkout, orders). Only the real estate tools apply here.
  • Calendar files: the storefront generates .ics files for a customer's own showing and for open houses.
  • Feeds and syndication: SourceFeed and ListingSyndication hold status that staff record. queueSourceImportCommand and queueSavedSearchAlertCommand write outbox events. Events stay pending until your own process dispatches them.

More

On this page