Overview  ·  ← Project plan
Spec for review · v3 · updated 2026-08-10 with Brent's maintenance-KPI + resident-follow-up decisions · not locked

Lead & comms architecture, sequenced

Two channels: Ownership (the Foothold AI agent, sales + brand + the owner relationship) and PM operations (Liberty, native in Entrata, owns service + SLA). Every ownership message is signed – Foothold so the guest always knows who they're hearing from.

Every Entrata write leg on this page is BLOCKED (restated 2026-08-05). Constitution rule 2 makes the integration read-only against any live production property, so every write described below (guest cards via sendLeads, thread notes via updateLeads, work orders via sendWorkOrders) is design-only until Brent signs off writes for that specific property in writing. Reads are unaffected, availability, pricing, leads, move-in dates, lease data, work orders and consent state all pull normally, so the data architecture in section 8 is live as written. Deferred, not cancelled: the design intent below stands, and in the interim every touch logs to the local outbox and replays into Entrata once writes are approved for that property. Deferring the write isn't permission to skip the logging, it only changes where it lands for now, and the local record has to be complete enough to replay. Two write services are live on the key (sendLeads and updateLeads). Maintenance is a READ surface now, not a write one: Liberty put the whole maintenance group on the key on 2026-08-03 and getWorkOrders + getWorkOrderPickLists are verified returning production data, but sendWorkOrders and updateWorkOrders were withheld in that same grant, so their absence is a deliberate per-method state, and a reachable maintenance surface is a reason to be more careful, not less. Brent declined to have the two writes removed on 2026-07-31, so assume write capability is permanently present and there is no vendor-side guard by choice. Our own deny-by-default read-method allowlist, enforced at the one function that issues requests and failing safe, is the only control that ever prevents a write. Rule 12 rides at the same choke point: never ingest, persist or log an SSN, and store birthdays as month and day only.
📌 DECISION, Brent, 2026-08-05: rule 2 WILL be lifted, but only on NPI-OWNED properties, and only at go-live. Nothing above is loosened today. The read-only rule comes off NPI-owned properties when Foothold goes live on them (Aspen, then Summerset). That is a planned change, not a present one. Three conditions must ALL hold before a single write is issued: (1) NPI owns the property, (2) Foothold is live on it, and (3) Brent has signed off in writing naming that property and those methods. Until all three hold, every write leg on this page still queues to the outbox. 🚫 Owyhee Park is NOT covered and is not expected to be. It is Liberty's building, not NPI's, and a property NPI does not own stays read-only regardless of what the key is permissioned for. Everything verified so far was measured at Owyhee, so a working probe is not a licence to write. ⚠️ The deny-by-default allowlist does not go away, it becomes PER-PROPERTY. Same single choke point, same fail-safe; what changes is that a named property can carry a named set of permitted write methods instead of the list being empty everywhere forever. A missing, unparseable or unrecognised property config still means read-only, and it gets built that way from the first line of client code, because retrofitting a per-property gate onto a global bar is exactly where the accident happens.

1. Where guests come in, and who answers

All text-based contact goes to the Foothold AI agent (one brain across web form, chatbot, email, SMS). Live phone calls forward to LAG.

→ Foothold AI agent (ownership)

  • Apartments.com / Zillow inquiries (lead email routed to agent)
  • footholdboise.com/[property] inquiry form
  • Website chatbot (same agent)
  • Email to the ownership address
  • SMS to the ownership number
One number + one brain. Acquisition, brand, first-touch, escalation. NPI controls its personality + context.

→ Liberty / LAG (operations)

  • All live phone calls (sales or service) forward to LAG, a human answers
  • Listing maintenance (Apartments.com / Zillow, in Entrata)
  • All service / maintenance / rent / lease contact
LAG lives entirely in Entrata. A forwarded call gets logged by LAG natively.
Voice, v1: all calls forward to LAG. After any forwarded call, the agent texts ~15 min later: "Just want to make sure you connected with someone and got your issue resolved, any feedback? – Foothold". Simpler than detecting missed-vs-answered, and it's on-brand. AI voice stays parked for v1, a sequencing call (see section 9).

2. The sequence

Left to right is time. The red line is the handoff. Entrata is the record underneath the whole thing; GHL (ownership's marketing brain) gets fed in parallel.

Stage →
1. Discover
2. First contact
3. Capture
4. Qualify & book
5. Operations
6. Retention
Ownership
(AI agent)
Brand site + the AI scorecard that grades the listings & feeds the PM
Agent answers form / chat / email / SMS instantly, 24/7
Qualifies + captures the minimum, then queues the guest card (write deferred)
Answers questions, offers tour windows for Liberty to confirm, or sends the apply link
Mostly handed off. One post-tour feedback text (~1-2 hrs after): "questions, or where can we improve?"
Recognition touches + monitors escalations and the PM's SLA
PM / LAG
(Entrata)
Maintains the Apartments.com / Zillow listings (in Entrata)
Live phone calls forward here
Sees the guest card appear in Entrata (once writes are signed off)
Watches the thread in Entrata
Runs the tour, leasing, move-in, all service, natively
Day-to-day service, maintenance, renewals
ENTRATA
(the record)
Listings + availability live here → syndicated to the ILSs
(native guest-card feed from Apartments.com)
Guest card created · sendLeads
BLOCKED (rule 2), queues to outbox
Every message logged · updateLeads
BLOCKED (rule 2), queues to outbox
LAG works natively · full thread visible
Service history (LAG native) + recognition tasks (floating work orders)
our writes BLOCKED, queue to outbox
▲ HANDOFF: tour scheduled OR application submitted
Ownership active LAG active Entrata record grey = idle that stage
The minimum-info gate (stage 3): we don't create a guest card until we've captured first name + one contact method (email or phone). (Entrata's sendLeads only hard-requires the property ID, but a card with no way to reach them is garbage. The legacy ILS/MITS feed also wants a last name.) Until then the chat lives in NPI's stack only. Rule 2: sendLeads is a write, so today the card queues in the outbox instead of reaching Entrata. The gate still governs, it decides what we're allowed to queue.
GHL gets everything. Every lead from every source (Apartments.com, chatbot, site, email, Entrata) is pulled into GHL too, that's the NPI-owned marketing brain running drip sequences, property updates, and recognition. Entrata is LAG's operational record; GHL is ownership's marketing record. Both get fed, the agent keeps them in sync.
Consent gates every send (verified 2026-08-05). getMarketingPreferences returns per-customer optIn by channel (email, phone, postal_mail, sms) with a consent type, and it is a precondition on the send path, not a courtesy. Read consent before an outbound touch, never infer it from having an address or a phone number, and treat a missing preference as "don't send" rather than "no objection." Postal mail is a first-class consent channel, which matters here because most of the recognition engine in section 4 is physical mail. ⚠️ The same customer can hold BOTH a LEAD and a RESIDENT preference set, recipientType is a lens on the person and not a partition of them, so a one-sided check is an incomplete check: read both sides for anyone who exists on both and fail closed on the union. ⚠️ Cache it, never call it per lead: the consent pick list is the tightest hourly bucket on the whole key (60/hour, 10/minute) and one read per lead exhausts the hour on the first sixty. One read a day is generous. Parse defensively too, the rows aren't uniformly shaped (phone and postal_mail can come back with no consentType key at all). Reading a preference is not recording one, writing consent back would be a write and stays barred by rule 2.
Hard rule, full PM visibility: every one-to-one touch with a contact, a recognition card, a personal text, an escalation reply, anything we say to them directly, is logged to the Entrata thread (updateLeads), so Liberty sees exactly that Foothold reached out and what we said. Nothing happens with a contact that the PM can't see. The only exception is bulk marketing (drips/broadcasts), which don't log per-contact. Deferred while rule 2 holds: updateLeads is a write, so on a live production property this logging is deferred, not active. Touches log to the local outbox and stay there, and the relay replays them into Entrata once writes are signed off for that property. The obligation to log is unchanged, only the destination is, and the local record has to be complete enough to replay. Until then "the PM sees everything" is on hold, not waived, so anything Liberty genuinely needs in the moment goes to them by email.
Tours: the booking is manual, the configuration is not (answered 2026-08-03, verified 2026-08-05). Liberty's agents book tours themselves off a call or an email, because each one runs a portfolio of properties and has to arrange their own drive time, or they hand out lock-box info for a self-guided tour. getCalendarAvailability does return real, maintained configuration: 30-minute appointments, a hard 120-minute minimum lead time on both tour types, agent-guided Mon–Fri 08:00–16:00 MST, self-guided all 7 days 06:00–18:00 MST, one at a time, with a 7-day maximum query window. So the agent offers windows out of that config and hands the confirmation to a human. ⚠️ An open window is not proof of an open calendar, a tour booked in an agent's head never reaches Entrata, so the agent never asserts a slot is free and double-booking prevention stays ours. ⚠️ Tour hours (08:00–16:00) and the property's documented office hours (M–F 9:00–5:00) disagree by an hour, read both and derive neither. Self-service tour scheduling (Calendly-style, drive-time buffers on either side, the agent nudging leads toward adjacent slots so an agent's day routes efficiently) is Brent's later exploration, explicitly not v1 and not on the critical path.
Pre-leasing on turn time: the moment a unit gets notice to vacate, the system reads the vacate date, adds Liberty's turn SLA, and markets the computed available date to the waitlist: "available [vacate + turn], priority to applicants who can move in closest to that date." ⚠️ The turn length is an input we do not have yet, and nothing has been agreed. This callout used to say "Liberty's agreed turn SLA (say 3-4 days)"; nothing was ever agreed, and Liberty ask #10, confirm the make-ready SLA, has still not been sent. The only data we have points the other way: on probe day, MakeReady was one day before AvailableOn on both available units. Two samples, so it settles nothing either, which is the point. Read the ready date from Entrata where it exists, treat any turn length as a configurable placeholder until Liberty confirms one in writing, and don't put a number in front of a partner before then. Fair housing: yes, this is fine. Move-in timing isn't a protected class, it's a neutral vacancy-minimizing rule. Keep it a written policy, screen everyone by the same criteria first and apply the timing rule among the qualified, and stay open to a reasonable accommodation (a family or disabled applicant who needs a few extra days). Just don't set the window so tight it predictably excludes families/disabled, that's the only disparate-impact risk.
The waitlist matching ladder (delivers the Insider 48-hour promise): when the available date computes, the engine matches the waitlist on the optional profile (beds/baths, budget, pet, rough timing) and works down:
  1. Matched Insiders get the 48-hour early window, texted first, ordered by stated timing closest to the date, waitlist join date as tiebreak.
  2. Broader waitlist when the early window closes.
  3. Public listing (Entrata ILS syndication fires).

Priority attaches at application, not interest. The profile's timing is only a soft signal for who to text first; the move-in date that counts, and qualification, come from a completed application. The written allocation rule: among qualified applicants, the completed application that can move in closest to the available date wins. Neutral, mechanical, identical for everyone.

3. Who owns what

ChannelOwnsHard line
Ownership
Foothold AI agent
Sales + acquisition + brand. First-touch, qualifying, booking, the listing scorecard, the owner relationship, recognition, and catching escalations (then handing them to Liberty, who owns resolution + SLA)No rent/lease admin, no operational or pricing promises. Service tickets: today, and permanently at Owyhee Park, the agent points the resident at Entrata's Resident Portal and does not take the request. Target state on NPI-owned properties once live: the agent files the work order itself, after verifying the resident (section 5). Two hard gates first, sendWorkOrders and the rule 2 lift.
PM operations
Liberty, in Entrata
Operations + voice + listings. Tours, leasing, move-in, maintenance, rent, lease, renewals, all service. Owns all voice/phone (sales calls forward here too) and maintains the listings.Never leaves Entrata

4. Recognition touch map (proposed)

My suggestion, building on your leanings: mail for the meaningful and celebratory (and anything with a gift); text for the light, timely, relational. A handwritten card by mail is rarer now, so it lands harder. All from ownership, signed Foothold. (Researching the best card+gift-card API provider now.)

MomentChannelWhat + tone
Move-in (welcome packet)Mail + textWelcome card + $25 gift card (local). First impression. Once, at move-in.
Resident birthdayMail + textHandwritten card + $25 gift card (Amazon or local). 1-2/yr (per adult on the lease). Data status 2026-08-05: viable, not solved. Date of birth comes only from getLeads, it's captured at application and never propagates to the lease or resident record, and measured coverage is 57 of 88 current residents (65%). The gap splits two ways: 18 current residents never appear in getLeads at all (no window recovers them) and 13 appear with the field empty. So the Welcome Profile is a REQUIRED component covering the other third, not an optional fallback. Store month and day only, never the year (rule 12).
Before renewal (every unit)Mail + textThank-you card + $25 gift card, pet-themed (Chewy) if they have a pet, generic if not. Goes to everyone, not just pet households. Lands ~90 days before renewal, ahead of the ~60-day renewal offer, gift-then-offer reads genuine; offer-then-gift reads like a bribe. The pet half is no longer waiting on data: getLeases carries 34 pet records across 30 of the 52 leases with the pet's name plus breed, color, gender, age, weight and move-in date, joined to the resident on customerId, the same key that joins leads to leases. Capture the whole record, not just the name, breed and age are what make the copy specific. 🚫 isAssistanceAnimal is a HARD fair-housing exclusion, not a matter of tact: assistance and service animals are not pets, they come out of the pet gift and out of every pet-themed message, and a missing or unparseable flag excludes the record.
Tenure milestone (1 / 3 / 5 yr)MailCard + the experience gift per the tenure ladder. The big ones.
New babyMailCard (+ optional meal box). Celebratory, never a gift card.
Engagement / marriageTextLight congrats, no gift. Timely beats formal here.
Death / hardshipMailHandwritten sympathy card, no gift card (it'd cheapen it). Sincerity only.
Property updates & changesText / emailProactive heads-up on anything happening (paving next week, new package room, landscaping refresh). A small thing that makes residents feel like they matter and the place is theirs. Via GHL broadcast from ownership.
Cut, and it stays cut: the automated post-service-issue satisfaction survey. It reads impersonal and gets unsubscribed. We just leave the service-call card, and they have the ownership number if they want to reach us. The 2026-08-10 decision below does not reverse this.
💬 DECISION, Brent, 2026-08-10: the resident follow-up after a service call is an AI-generated personal message about their specific issue. His words: "an AI generated personal message about their specific issue, 'Did your back door get fixed?'" This is NOT the survey above. That cut stands. This is the lighter personal check-in, and it is exactly the thing that survives the objection that killed the survey: specific, personal, and from the ownership number rather than a form. It does not conflict with "watch the scoreboard, not the plays" (section 5, step 3), and the reason matters: the message goes to the RESIDENT, not to Liberty. Nothing per-ticket is sent to, demanded of, or logged against Liberty. The posture is "we care about you," not "we're auditing Liberty," and per-ticket outbound to a resident is a different act from per-ticket policing of a vendor.

What it needs, all readable today with no new permission: maintenanceProblem, maintenanceLocation and maintenanceDescription off getWorkOrders. Four mandatory filters, all decided elsewhere and inherited here: (1) skip maintenanceRequestFrom: "Entrata PaaS", a system-generated make-ready must never fire a resident message; (2) collapse parent/child on parentMaintenanceRequestId so one turn sends one message, not several; (3) clear the getMarketingPreferences consent gate on both the LEAD and the RESIDENT lens, and a missing preference means don't send; (4) triage on maintenanceProblem and maintenanceLocation, never maintenancePriority (780 of 785 tickets are Medium).

⚠️ A new risk comes with it, and it gets a written rule: the message quotes the resident's own issue back to them, so a wrong or stale detail is worse than a generic message. "Did your back door get fixed?" lands well when it's right and badly when it isn't, and the pick-list values are terse and don't always render into natural language ("Make Ready" is the obvious one, and it's exactly the system-generated case filter 1 already excludes). The rule: the AI renders from the actual field values and never invents detail. If problem and location won't form a natural, confident sentence, fall back to a generic check-in or don't send. Specificity is the point, but a confident wrong guess costs more than it earns. Constitution rule 8 (fair housing) applies, as it does to any AI-generated resident-facing message.

📌 The touch can't be logged into Entrata, and the reason is the RESIDENT record specifically. sendLeaseActivities, the only method in the 128 that appends an activity to a resident / lease record, is not on the key. So it logs to Foothold's own store. ⚠️ This strengthens the case for the sendLeaseActivities ask: a resident-level service touch is exactly what it would carry. ⚠️ CORRECTION 2026-08-10: this passage used to open with "there is no lead-event write method anywhere in the 128-method spec." That was wrong and it is withdrawn. updateLeads r2 and sendLeads r1 both carry an events array (typeId, date, type, free-text comments) and both are on the key. The decision and the destination are unaffected, because this message goes to a resident, not a lead, and the lease-level half of the reasoning stands on its own. Only the stated rationale was partly wrong. 🚫 And nothing about it loosens anything: appending a lead event is a WRITE, rule 2 governs it completely under the same three conditions, and no write is authorised by a correction about what the API can do. 🚫 Scope: NPI-owned properties once Foothold is live on them. NOT Owyhee Park, which is Liberty's building with Liberty's residents. Everything measured so far was measured at Owyhee, and reading their tickets does not licence messaging their residents.

Still open, deliberately: the completion trigger itself (poll getWorkOrders versus the workorder.status.update webhook, and which maintenanceStatus values count as complete enough to ask), the timing after completion, and 🆕 whether this message and the review ask in section 6 are one touch or two, which nothing has addressed anywhere. Flagged, not decided.
Fulfillment (researched): two providers cover all of it. Handwrytten mails a real handwritten card with a physical gift card enclosed via API (~$32-34 for a card + $25 Amazon card; ~$4 card-only for sympathy). Tremendous sends a digital gift card by text, free to use, Amazon + 2,000 options, fund by ACH to skip the 3% fee. Skip the ABM platforms (Sendoso, Reachdesk), they carry $15-25K/yr minimums for what we'd do with two API calls. Every send also logs a note to the Entrata thread, so the PM sees the touch. (Blocked by rule 2 today: cards and gifts still go out, the Entrata note queues in the outbox and replays once writes are signed off.) 🔄 And the method that would carry it is now a NEAR-TERM ASK, decided 2026-08-05 (Brent deferred it earlier that day and reversed himself the same day). sendLeaseActivities is the only method in all 128 that appends an activity to a record, it is a resident/lease-level write, and it would put these touches in Liberty's system instead of their inbox. It is not on the key, so it takes a new ask, and that ask goes out in the same email as the sendWorkOrders ask: that one is the larger and more consequential write grant, so bundling the low-blast-radius activity note into the same conversation is one round trip instead of two. Frame it to Liberty as what it is for them, less mail, not more. ⚠️ The grant is not the authorization. It is a write, so rule 2 still governs it: the per-property lift plus Brent's written sign-off naming the method, and at Owyhee Park it stays unused even if granted. Interim answer, no permission needed, and permanent at Owyhee: log the touch in Foothold's own store and close Liberty's visibility gap deliberately with a scheduled digest to the property inbox or a read-only Foothold view.
Gift-card touches (spec for now): welcome, birthday, before-renewal. All three carry a $25 gift card. Both legs are consent-gated separately: postal_mail for the card, sms for the text, they're distinct channels in Entrata's consent record and an opt-out on one doesn't imply the other. Each one gets a follow-up text the night the gift card lands, "just want to make sure you got your package", which confirms receipt and opens the review-ask door (section 6). The other touches carry no gift card (sympathy = card; marriage / property updates = text). Tenure-milestone experience gifts + new-baby boxes are a later add.
$250 / $250 resident referral. Refer a friend who signs a lease (within 12 months of the code) and you both get $250 in rent credit, tracked by code from first touch so it pays even months later. The agent surfaces it at the natural highs (the move-in kit, after a recognition touch, in the relationship cadence) and it rides on the referral flyer. The off-platform, resident-to-resident demand lever.

5. If a resident brings ownership a problem

Liberty owns maintenance, SLA, and resolution, that's what they're paid for, and we don't second-guess it ticket by ticket. But a resident who texts the ownership number with a problem can't be left hanging. Updated 2026-08-05: the intake half of this changes on NPI-owned properties once they're live, see the decision at the bottom of this section. Liberty still owns the work.

  1. Acknowledge, instantly: "Got your message. This is our property manager Liberty's responsibility and they're great at taking care of you. We've passed it to them and they'll be in touch shortly. – Foothold"
  2. Hand it to Liberty by email, and let them run it. Answered 2026-08-03 by Alex Costa: a resident or prospect who wants a person goes to Liberty's direct leasing / manager team by email, and adrienne@libertyassetgroup.com is the right address today. They run Google and Breakroom chats internally but prefer direct email, so build the notification leg as email and don't invent a chat integration. ⚠️ The assistant-manager / leasing team is being restructured and more addresses are coming, so the recipient is per-property config, never a constant in code. Store it with the rest of the per-property config, expect it to become a list per property, and re-verify it when a property is added or a reorg lands. No nightly SLA-policing, that's their job, not ours. ⚠️ Qualified 2026-08-10, not withdrawn: Brent's decision to build an aggregate responsiveness KPI (step 3) doesn't cross this line, because measuring the aggregate is not policing. Alerting on, or chasing, an individual ticket still is. Email is the path today, and at Owyhee Park it is the path permanently: sendWorkOrders has never been granted (it was still withheld on 2026-08-03 when Liberty put the maintenance reads on the key), and it's a write under rule 2 either way. 🔄 Changed 2026-08-05: it is now a live ask, for NPI-owned properties only, and turning that leg on needs both a Liberty grant and the rule 2 lift for that property. The work-order intent queues in the outbox meanwhile.
  3. Watch the scoreboard, not the plays. We don't monitor individual tickets. If Liberty slips, it shows up in the aggregate, the resident-rating trend, review velocity, and renewals on the scorecard. That's the owner's altitude. ✅ AFFIRMED by Brent 2026-08-10. He chose aggregate over per-ticket policing, so this line stands exactly as written, and it's now a decision he has actually made rather than an unattributed design-intent line. (It's still design intent and not a Constitution rule, so whether to promote it is a separate open question.) The aggregate has a real source now: getWorkOrders reads production tickets (status, priority, problem, location, completedOn, scheduled dates, assigned employee), so responsiveness is measurable read-only without touching a single ticket. Two cautions before anything counts or messages off it: filter on maintenanceRequestFrom, records marked Entrata PaaS are system-generated (make-readies among them) and must never fire a resident-facing touch, and collapse parent/child on parentMaintenanceRequestId before counting, a child record is not an independent ticket.
📊 DECISION, Brent, 2026-08-10: build the maintenance responsiveness metric, as an AGGREGATE, and it serves two purposes. His words: "If that KPI is genuinely good we can use it in marketing, and if it is bad then it is an accountability metric for LAG." This is new. The design carried no resolution-time metric at all, only the first-contact target. It doesn't overturn step 3, it's the thing step 3 was always pointing at: measuring the aggregate is not policing; alerting on or chasing an individual ticket still is.

⚠️ Dual-purpose is a build constraint, not colour. Good numbers become marketing proof for the Foothold thesis; bad numbers become an accountability metric in the Liberty relationship. Both uses demand the same thing: one definition, written down once, computed the same way every time, and never cherry-picked by window. A number used in both directions can't be redefined when it's inconvenient. Write the definition before the first chart.

How it computes, and it needs no new permission and no ask to Liberty: getWorkOrders carries completedOn (observed in production 2026-08-05) and createdOn (in the vendor spec, not yet observed), plus createdOnFromDate / createdOnToDate / completedOnFromDate / completedOnToDate request filters. Read-only, no rule 2 exposure.

Build cautions, because a naive computation would be wrong: skip maintenanceRequestFrom: "Entrata PaaS" (7 of 785, turns not resident issues); collapse parent/child on parentMaintenanceRequestId (312 of 785 are children) or one turn counts several times; don't segment on maintenancePriority (780 of 785 are Medium, no signal), use problem and location; ⚠️ createdOn is spec, not observed, so verify it comes back populated before building on it, a single read-only call settles it and if it's empty the metric needs a different clock and the design changes; and consider reporting in-hours versus out-of-hours separately, since office hours are M-F 9:00 to 5:00 with no weekend coverage, so roughly 76% of the week has no staff and a raw aggregate hides that.

Left open on purpose: the target or threshold isn't set. ⚠️ The 24hr / 4hr number on the scorecard is a FIRST CONTACT target, parked at Phase 2 pending Liberty's commitment. It is not a resolution target. And whether this metric joins the six hero KPIs or stays a feeder line is undecided.
Why the restraint: re-policing every ticket would make Foothold a second management layer, defeating the point of paying a pro PM. As long as Liberty's at or above market on responsiveness, we keep the resident relationship warm and let them operate. (If NPI ever vertically integrates PM, this changes.)
🔧 DECISION, Brent, 2026-08-05: on NPI-owned properties, once Foothold is live on them, the agent FILES the work order itself. This reverses the "Foothold must never be the intake" position. Build toward it. Two hard gates come first, and neither is cleared: (a) sendWorkOrders is NOT on the key and is a new ask to Liberty that has never been made (the maintenance group landed 2026-08-03 with two reads and neither write), and (b) rule 2 must be lifted for that property, meaning NPI-owned, live, and signed off in writing.

⚠️ VERIFY THE RESIDENT BEFORE FILING. An unverified ticket sends a real technician to a real unit. Best identifiers, in order: phone on file is the strongest and cheapest (getLeases customer phone, getMitsLeases Phone, and an inbound text or call from a matching number is a strong match with zero friction); unit number plus one more factor when the number is unrecognised; and birthDate is a weak primary that must NEVER be the only gate, because coverage is 65%, so a third of residents could not pass it at all. Fail closed: no match means hand off to a human, never file the ticket anyway.

🚫 Until both gates clear, and permanently at Owyhee Park: point residents at Entrata's Resident Portal and do not take the request. That is evidence, not tidiness. Of Owyhee's 785 work orders, 390 (50%) already originate in the Resident Portal, 388 are staff-entered and 7 are system make-readies, and all 71 work-order locations are resident-portal-enabled. Residents have a working intake path that lands directly in the system Liberty's techs work from. Taking a request we cannot file means it sits in a queue while the resident believes it was filed.

The after-hours problem does not go away when the agent can file. A ticket filed at 11pm Friday still waits for Monday unless someone is on call, against office hours of M-F 9:00 to 5:00 with no weekend coverage. Genuine emergencies get the phone path, never a chat handoff and never an email, before and after the rule lifts.

Two build facts for whoever specs the maintenance engines: priority is effectively unused, 780 of 785 tickets are Medium (3 Very High, 1 High, 1 Very Low), so triage on maintenanceProblem and maintenanceLocation, not maintenancePriority; and 312 of 785 are child tickets carrying parentMaintenanceRequestId, so collapse parent/child before counting or sending or one turn generates several resident touches.

6. Social proof engine (gathering reviews + testimonials)

Reviews are a conversion multiplier on both ends: they lift ILS rank (a listing with ≥1 review gets up to 50% more leads; renters filter on 4.5★) AND they lift the site funnel (land → pick a unit → apply / join the waitlist). So gathering them runs like a machine, timed to the moments residents are happiest.

The line that keeps it legal: trigger on EVENTS and ask everyone at that event, never filter by sentiment. Asking all residents after move-in is fine; asking only the happy ones, or screening a review before it goes public, is review gating, which Apartments.com, Google, and the FTC prohibit (Apartments.com flags it for 90 days). Timing to emotional highs is legitimate; sentiment-gating isn't.
Trigger (emotional high)WhyAsk
~2 weeks post-move-inSettled in, welcome packet landed, strong first impression. (Not day 1, they haven't experienced us yet.)Auto-text after the welcome packet
After a recognition touchSurprised + delighted by the card / gift, but keep it light.A soft "just want to make sure you got your package" that opens the door, not a hard ask
Lease anniversary / renewalCommitted and loyal, they re-signed.Auto-text at renewal
The machine: the worker watches Entrata (move-in date, anniversary) + our DB (recognition sends) and fires the ask by text from the ownership number with a smart link to Google + Apartments.com, after the consent check in section 2 (SMS opt-in, both the LEAD and RESIDENT sides, no preference means no send). The same ask invites a short on-site testimonial (a line + optional photo). Public reviews flow back into the ILS scorecard (rank) and onto the site; testimonials feed the footholdboise.com funnel (homepage + property pages) to lift land-to-apply. Every ask is logged to Entrata. (Rule 2: the ask still sends over Twilio, the Entrata note queues in the outbox until writes are signed off. The event triggers, the 12-month suppression, and the monitoring are all reads and run normally.)
Don't spam. We ask at events, but never re-ask a resident who's left a public review in the last ~12 months, and we space the touches so it never reads like a campaign. One good ask at a real high beats five nags.
The move-in packet is a Foothold touch, personalized. A handwritten welcome card + a local gift card mails from ownership via Handwrytten at move-in. The "First Week in Boise" booklet only goes to renters we know are relocating, a 30-year local doesn't need it, so we tailor by what we actually know about them (more custom = better). It sets up the ~2-week review ask. Possible personalization source, TBD: if Liberty's phone system exposes call transcripts, they're a rich (and sensitive) well of resident life-context, consent-gated, worth asking Liberty about.

7. Edge cases

Green = locked. Amber = decided, pending an Entrata/API confirmation (research in flight). A Write deferred tag means the decision itself holds but its Entrata write leg is blocked by Constitution rule 2 (see the banner up top), so it queues to the outbox and replays after per-property write sign-off.

ScenarioHandlingStatus
Resident texts ownership # for a ticketToday, and permanently at Owyhee Park: redirect, don't take the request. Point them at Entrata's Resident Portal, gently: "service requests go through the resident portal so the techs see them straight away, here's the link." That path already carries 50% of Owyhee's 785 work orders and lands in the system Liberty works from. The number's more visible, so this'll happen, it's fine. Interim: the redirect text still sends, the Entrata log queues in the outbox and anything Liberty needs to act on goes to them by email. 🔄 Target state on NPI-owned properties once live (decided 2026-08-05): the agent verifies the resident and files the ticket itself. Gated on sendWorkOrders (never asked) and the rule 2 lift. Verification order: phone on file, then unit plus one more factor; DOB is never the only gate (65% coverage); fail closed to a human.Locked Write deferred
Guest plays mom & dadBoth read the same Entrata record; agent reads LAG's latest activity before replying and makes no ops/pricing promises; ownership routes back, never overrules.Locked
Any forwarded phone call~15 min later the agent texts to confirm they connected + ask for feedback. No need to detect missed-vs-answered.Locked
Emergency by text to ownership #The live path is phone + email, and it isn't temporary. An emergency alerts LAG by phone and by email to the leasing / manager address (adrienne@libertyassetgroup.com today, per-property config), and the work-order intent queues in the outbox. The priority vocabulary is real and readable: getWorkOrderPickLists is verified live and returns Priorities, StatusTypes, Categories, Problems, Locations and 12 named Liberty employees, property-scoped, so each property needs its own read. ⚠️ At 1,000/day it's the tightest daily bucket on the key, so cache it, one read per property per day, never one per ticket. Filing the work order stays design-only: sendWorkOrders is not on the key at all and is a write under rule 2, so it needs a Liberty grant and the rule 2 lift for that property, and an unverified work order sends a real tech to a real unit. 🔄 Decided 2026-08-05: it is now first in line, as a live ask for NPI-owned properties.None of that fixes the after-hours problem, and the emergency answer doesn't change either way: a ticket filed at 11pm Friday still waits for Monday unless someone is on call (office hours M-F 9:00 to 5:00, no weekend coverage), so a genuine emergency gets the phone path, never a chat handoff and never an email, before and after the rule lifts.Locked Write deferred
Agent down / API write failsBackup email notifies the Liberty PM with the lead + message context, nothing lost while it's down. Under rule 2 this isn't the fallback, it's the primary path for anything Liberty has to act on, since no write reaches Entrata today. Build it first, not last.Locked
Handoff pointOwnership owns through tour completed or application submitted; LAG owns everything after. Plus the one post-tour feedback text.Locked
Tour confirmation + no-showAgent owns the gap between booked and showed: a confirm text the day before or morning of ("reply to confirm"). If unconfirmed by a couple hours out, one nudge, then the agent notes it "unconfirmed" on the Entrata guest card and LAG decides whether to make the trip. No-show → agent re-engages next day ("want to grab another time?"). Remember the tour was booked by a human, Liberty's agents schedule around their own drive time, so "booked" means Liberty's confirmation, not a slot the agent claimed. Rule 2: the note queues in the outbox, so until writes are signed off the unconfirmed flag reaches LAG by email instead. They still get the call in time to skip the trip.Locked Write deferred
Agent out of its depth mid-conversationThe agent never busks. It tells the guest "let me get a person to follow up on that," emails Liberty's direct leasing / manager team (adrienne@libertyassetgroup.com today, per-property config, they're restructuring), logs an urgent note on the Entrata guest card, and Liberty takes the conversation over from their channel. Escalation human = LAG, not Brent. Rule 2: the urgent note queues in the outbox, so the live hand-off runs entirely on email until writes are signed off. An escalation can't sit in a queue waiting for a permission. Knowing the address still doesn't tell us who reads it at 10 PM on a Friday, the after-hours fork is open.Locked Write deferred
The application itselfConfirmed: it's Entrata's ProspectPortal, live + self-service, and the agent never takes an application itself, it hands out Liberty's hosted URL per property ([property].prospectportal.com/.../application_authentication/), zero friction, apply anytime. ⚠️ The per-property apply link is the one item still open with Liberty (asked 2026-08-03, unanswered): whether every property has one. Don't chase it with a second email. Abandoned-app follow-up: poll getLeads for an in-progress status gone stale, and take the status IDs from getLeadPickLists (62 LeadStatuses, pin them) rather than submitting a test application, which would put a fake applicant in front of a real leasing agent.Locked Apply link open
Which number whereOwnership # on website + marketing. On service surfaces (lease, packet, portal, service-call card), LAG # is bold/front-and-center, with a small footnote: "Feedback for ownership? [Foothold #]."Locked
Ownership # gets service tickets / serves residentsYes by design, it's the visible, relational number (birthday texts, life events, "got your card?"). Service tickets that land there get auto-saved + redirected to the Resident Portal, which is the right answer today and permanently at Owyhee. On NPI-owned properties, once live and once both gates clear, the redirect becomes a filing: verify the resident, then create the ticket (decided 2026-08-05). Foothold stays the forward-facing brand keeping the PM honest.Locked
Do scorecard + gifts need PM tasks in Entrata?No tasks, but yes visibility. Gifts auto-send (Handwrytten / Tremendous) and the agent logs a note to the Entrata thread ("mailed birthday card + $25 gift card"), so the PM sees it, no action needed. Scorecard/metrics = a weekly email (or ops-site login), not in Entrata. Actual service tickets/emergencies on the ownership channel get written as work orders LAG acts on. Both Entrata legs (the gift note and the work order) are blocked by rule 2 and queue to the outbox. The gifts still auto-send, and the weekly email is unaffected since it never touched Entrata. ⚠️ The gift-note leg carries a second block, and the ask against it is now moving: sendLeaseActivities is the only method that could append that note, it's not on the key, and Brent put it on the near-term ask list on 2026-08-05, reversing his own deferral from earlier the same day. It rides in the same email as the sendWorkOrders ask. ⚠️ A grant would clear the permission block only, rule 2 still gates the call per property in writing, and at Owyhee it stays unused either way. So the interim visibility path (a digest to the property inbox, or a read-only Foothold view) is what actually closes this in the near term, not a permission.Locked Write deferred

8. Context & data freshness (the EliseAI lesson)

This is the whole ballgame. Liberty fired EliseAI because it ran on stale availability data, told prospects units weren't available when they were, and sent leads away. Not an "AI can't do it" problem, a bad-context problem. The Foothold agent can never make that mistake.

The fix is architectural, not just a better prompt. Foothold gets its own datastore (Supabase / Postgres) the agent reads from, kept fresh from Entrata by a background sync. Freshness is tiered, because nightly is fine for amenities but fatal for availability:

DataFreshnessHow
Availability + pricingNear-real-timeBackground poll every few minutes into Supabase, plus a live re-check of the specific unit right before the agent quotes it or sends the apply link. This is the EliseAI-proof.
Floor plans, amenities, property factsSlow (nightly)Nightly Entrata sync.
Lead + conversation stateReal-timeLives in Supabase / GHL, updated as it happens.
Resident facts (lease, pets)DailyDaily pull for the recognition engine. Lease facts and named pet records come from getLeases; date of birth comes only from getLeads (65% of current residents, the Welcome Profile covers the rest); household grain and customer IDs come from getMitsLeases. Strip SSN and truncate DOB to month + day at the transport boundary (rule 12).
Consent state (per channel)Daily, cachedgetMarketingPreferences read before any outbound touch, LEAD and RESIDENT sides, fail closed on the union. Cache the pick-list vocabulary, it's the tightest hourly bucket on the key (60/hour) and must never be called per lead.
Why cache instead of just hitting the API live per question? Latency isn't the issue (a live call is a few seconds, fine). The constraints are shape, not speed, and all three are measured (2026-08-05): (1) the gate is one account-wide serial queue, calls go one at a time and never concurrent, so two guests asking in the same second queue behind each other; (2) quotas are PER METHOD and wildly uneven, getUnitsAvailabilityAndPricing is generous at 600/minute while getWorkOrderPickLists is 1,000 a day and getMarketingPreferencePickList is 60 an hour, the two tightest buckets on the key, and getStatus at 6/minute can't even be a heartbeat; (3) Entrata takes weekly maintenance windows (Tuesday late night into Wednesday, plus unscheduled ones), so downtime is a normal operating condition. A cache absorbs all three: the poller keeps Supabase fresh inside the serial gate, the agent reads the cache, the live re-check is one targeted call at the commit moment, and last-good cache is what gets served while Entrata is down (alert only past ~2 hours stale).
The hard guardrail: the agent never states availability or price without a fresh read. If the cache is stale or the API's down, it says "let me confirm that and get right back to you", it never guesses. That one rule kills the EliseAI failure mode. (The agent's full personality + guardrails are their own work session; this is the data-architecture half of it.)
Telephony (planned): agent SMS + voice routing on Twilio direct (not GHL, which stays for bulk drips). A site-wide brand number + a per-property local 208 number, so an inbound text/call already tells us the property. Compliance: A2P 10DLC (Brand + Campaign via Twilio), consent capture + STOP handling, all numbers under one campaign.

9. The voice question (answered: forward to LAG)

Researched the real 2026 state of AI voice leasing. Verdict: forward calls to LAG for v1. Don't put AI voice on the front door.

You're right on both counts, voice isn't more stale, and it isn't a bigger fair-housing risk. Same brain + same guardrails says the same words whether typed or spoken, so both the data risk and the FHA risk are medium-agnostic. The one real delta was auditability (no transcript by default), and your transcript-to-Entrata idea closes it: log every call via updateLeads, same as a chat (a write, so it queues to the outbox like every other note until writes are signed off, which is moot for now since voice is parked for v1). With disclosure up front ("this is Foothold's AI assistant, ask for a person anytime") + one-tap transfer, voice is no riskier than chat. So v1 = forward to LAG is purely a sequencing call: prove the data layer + the agent's guardrails on the easier-to-tune chat surface first, then point the same proven brain at voice. A maturity decision, not a liability one.
The dependency it all rides on (restated 2026-08-05): not Liberty's permission. Liberty granted exactly two write services on 2026-07-31, sendLeads and updateLeads, against Owyhee Park, an occupied building, and we're deliberately not using them. (sendWorkOrders and updateWorkOrders were never granted and were still withheld on 2026-08-03 when the maintenance reads landed, so don't read the group's arrival as the writes being close behind. 🔄 Changed 2026-08-05: sendWorkOrders is now a live ask for NPI-owned properties, and it has never been made. updateWorkOrders is not part of it.) What's actually left is ours: (1) the read-only client, a deny-by-default allowlist at the single function that issues requests, failing safe, plus a property-ID allowlist at the same choke point (the key can see 98 other Liberty buildings), in place before the first live call. ⚠️ Build that allowlist PER-PROPERTY from the first line, per the 2026-08-05 decision at the top of this page: the rule lifts on owned properties, so a named property will one day carry a named set of permitted write methods, and a missing or unrecognised property config still means read-only. Retrofitting a per-property gate onto a global bar is where the accident happens; (2) live-availability reads working against the serial gate and the per-method quotas; (3) the outbox, so deferred writes accumulate in replayable form instead of getting dropped; and (4) Brent's written per-property write sign-off naming the property and the methods, which is the only thing that turns the write legs on, and only on a property NPI owns and Foothold is live on. Having the permission was never the authorization to use it. What is genuinely waiting on Liberty is four things, in order: Aspen and Summerset onto the key (the largest unowned dependency before the 8/31 close, and the gate on the whole write half since Owyhee can never carry it), the per-property application link (asked 8/03, unanswered), sendWorkOrders for owned properties (new 8/05, never asked), and 🔄 sendLeaseActivities (added 8/05 when Brent reversed his own deferral the same day, never asked). The last two go out in one email, not two, since sendWorkOrders is the larger grant and the activity note costs nothing extra to name in the same conversation. ⚠️ Neither grant authorizes a call, rule 2 still needs the per-property lift and Brent's written sign-off, and sendLeaseActivities stays unused at Owyhee even if granted. The honest build status: the design is done and the API is fully mapped, but nothing Entrata-facing is built yet, the service still runs in mock mode by choice. Design before code.