Proposed 2026-10-28, reworked 2026-10-29 on Fede's notes · pending Fede's review · nothing built, nothing turned on
Numbers show the last four digits only.
| What a caller gets | Camellia (JP&Co) | Western Slope | Situs Group |
|---|---|---|---|
| Greeting | Open: "Hi, it's Clara at Camellia Apartments, what can I help you with?" | Open: "Thanks for calling Western Slope Property Management. How can I help you?" | Open: "Thanks for calling Situs Group. How can I help you?" |
| Spanish offered | No (building line, no language answer) | 154 of 233 calls (30 days) | Yes |
| Asks for a person, office hours | Rings the office line ending 2043 (the building's leasing and "something else" lists) | A prospect, a named person, a vendor, or a resident's noise, billing or lease-break ask: Kat, then Bev, 20 s each. Else a message (repairs below) | Nothing set: a message |
| Asks for a person, after hours | A message | A message (repairs below) | A message |
| "Something else" | Office line ending 2043 in hours; else a message | Nothing set: a message | Nothing set: a message |
| Repairs | Clara's maintenance agent (live) | Clara (supervised); a caller who asks for a person goes to the call center ending 8684, at any hour | Clara (supervised) |
| Emergency | Emergency phone ending 0100; no on-call list, alert chain off | Alert chain: three staff in turn | Alert chain: the PMS on-call person, then two more people |
| Modules the voice line checks | Leasing, maintenance, renewals, resident services: all on | Leasing on, maintenance on (supervised); renewals and resident services off | Leasing and maintenance on (supervised), resident services on, renewals off |
Same at all three: office hours Mon to Fri 9 to 5; nothing set means a message; no office-phone fallback. On real Western Slope calls the "leasing, maintenance, or something else?" greeting last played Oct 10; now it comes only mid-call, when a caller gives nothing useful. Resident services on calls is the lease and billing agent, off at Western Slope since Sep 22.
Fede, Oct 28: one shape for "a very simple forward or a very complex operation." Oct 29: "you need to consider what modules are enabled… if they're enabled we transfer this to the sub-agent. If it's not enabled, we're not able to help with that so we would forward to human or take a message." Maintenance and the emergency chain keep their own lists and dial paths.
| Piece | Today | After | Status |
|---|---|---|---|
| Greeting | An open question at all three; the menu only when a caller says nothing useful | Open greeting and a topic classifier; the fallback menu goes too | Exists; one fallback to remove |
| Module check | Voice reads all four switches. Each "off" has its own rule: leasing off, Clara answers with no tours; maintenance off, call center or list or message; renewals or resident services off, the catch-all, else a message | One check for all four: on → that module's agent; off → the directory in hours, else a message (maintenance keeps its own) | Exists for all four, handled three ways; the single check is new |
| Where "who to ring" lives | About ten settings; transfers fixed at the greeting | One resolver, picked when the caller asks | New |
| Directory | Lists of a name and a number, one per call type; a link to the PropFlow person is optional | One directory: the same people, plus "asked for by name: ring me / take a message", "handles", "order" | Exists; three fields new |
| Staff phone at sign-up | Required in Team settings and ring lists; optional in the admin user API; not asked when accepting an invite | Required on every path that creates a staff person | Fix |
| Dial path | Our hunt line for routed calls; the call platform's built-in transfer still on the specialist agents | One dial path for every person or topic ask | Exists; specialists move |
| Person transfers after hours | A message, except Western Slope repairs (call center) | Same, now a stated rule; maintenance and emergency unchanged | Exists |
| Spanish prompt | "Para español, diga español" at some customers, not all | Same prompt everywhere; the caller's language is remembered for calls, texts and email | Fix, see Language |
| Maintenance, emergency | Own routes, lists and dial paths | Unchanged | No change |
| Question | Options | Recommendation |
|---|---|---|
| Who can be asked for by name? | (a) every "ring me" entry; (b) only entries on a topic rule; (c) anyone on staff | (a) |
| Spanish prompt at every customer | (a) yes, same wording everywhere; (b) per customer | (a), with the memory fix first |
| Field | Holds | Status |
|---|---|---|
| Who | A PropFlow person with a role, or a group line on their phone system, or an outside service | Exists as a name and a number; the person link is optional |
| Number | What Clara dials; never a line that forwards back into Clara | Exists |
| Asked for by name | "Ring me" or "take a message for me" | New |
| Handles | Topics this entry takes, including topics of modules the customer has off | New |
| Order | Place in line for a topic | Exists per list; new across topics |
| Hours | Company office hours; per entry later | Exists |
| Customer | Modules on (voice) | Entries | Rules |
|---|---|---|---|
| Camellia | leasing, maintenance, renewals, resident services | One on-site manager | Any person ask, in hours: that person. Else a message. |
| Western Slope (RingCentral) | leasing, maintenance | Kat then Bev as today, "ring me"; everyone else "take a message" | Any person ask, or a topic whose module is off, in hours: Kat then Bev. Else a message. |
| Situs Group (Crexendo) | leasing, maintenance, resident services | 13 staff and 2 VA seats from their phone-system export; group lines at onboarding | Topic rules per group line; by name, the person's choice; else a message. |
Shapes per Fede's ruling, Oct 28. Maintenance and emergency: unchanged at all three.
Fede, Oct 29: "right now how it works is you have to be added to PropFlow as a user with a role, right, and then that would make you discoverable in the system… we'll need to check if we require a phone number when people sign up. We should."
Fede, Oct 29: "once we interact with someone we should remember their language preference… we shouldn't ask again on the next call." Read on 2026-10-29 from the code, the change history and 30 days of real calls at the three customers.
| Layer | Today | Verdict |
|---|---|---|
| The offer "para español, diga español" | Plays only when the call is treated as company-wide or the company named Spanish in its settings. Camellia: never (0 of 118 calls). Situs: every call (20 of 20). Western Slope: 79 of 233 calls had no offer, the ones filed under an apartment building or from staff. | Inconsistent, by settings not by policy |
| Switching when the caller says "español" | Works; the language is saved on the person and later texts go out in Spanish (Western Slope caller ending 5740, Oct 9). | Works |
| Switching when the caller speaks Spanish without the word | From Sep 12 to Oct 27 a caller speaking full Spanish sentences got English; a robot test call to Situs on Oct 19 heard English for over two minutes. Fixed Oct 27; it overshot once on Oct 28 and flipped a mostly English call to Spanish. | Fixed Oct 27, thin proof |
| "Sí, gracias" to the offer | Real: Western Slope, Oct 27, 6:05 PM Central. Two words counted as too few to switch; Clara stayed in English, the caller kept speaking English, and English was saved at 6:07 PM. | By design, which you flagged: a Spanish "yes" is not a switch |
| Remembering on the next call | A caller saved as Spanish is greeted in Spanish but hears "for English, just say English" every call. A change merged Oct 29, not yet seen on a real call, stops offering Spanish to a caller saved as English, and almost everyone is saved as English after one ordinary call. | Half built: English is remembered, Spanish is re-asked |
| Text and email | Texts read the saved language. Email never reads or saves it; it answers in the language the email was written in. | Email missing |
Only 4 of 346 automated texts and emails to prospects and tenants went out in Spanish, all to one person. Clara's own written replies follow the caller's language; the sends made on her behalf read the saved language, which is usually missing.
| Layer | Today | Verdict |
|---|---|---|
| Email lead writing in Spanish | Clara replies in Spanish, but no language is saved, so the tour confirmation text and all four reminders went out in English (a Camellia lead, Oct 23: Spanish email and English text in the same minute). | Broken, most hit |
| Text lead writing in Spanish | The detector misses about half of ordinary Spanish texts (short words count as English, common words are not on its list), so texters are rarely saved as Spanish. | Broken |
| Guest card | The language lives only on our person record; it never reaches the AppFolio guest card or a note. Spanish website forms are missed. | Not built |
| Tour confirmation email and calendar file, application emails, work-order created, rating ask, vendor relays, renewal outreach | English-only copy, about 20 sends. | Not built |
| Tour reminders, quiet-lead follow-ups | Read the saved language and have Spanish copy; the follow-up still opens with the required English "It's Clara with…" and home facts arrive in English. | Partly |
| Spanish routing at Situs | Nothing in the product routes Spanish speakers anywhere; staff answer Spanish texts by hand in AppFolio. | Not built |
What to change (fixes, no switch): one offer policy at every customer instead of per-settings luck; a Spanish "yes" ("sí", "sí gracias", "sí por favor") counts as a switch; a saved language is used on the next call, text and email with no re-ask in either direction, and one sentence to switch back; the audit rows above marked broken get the same treatment.
| Vendor idea | Ours |
|---|---|
| Schedule: open hours, holidays | Hours (holidays later) |
| Destination: user, queue, menu, outside number, voicemail | Who and Number; a message is our voicemail |
| Rule: by time, caller, number dialed | Handles and Hours |
| Ring order, timeout, overflow to the next | Order, one at a time; next entry, then a message |
Unverified: RingCentral ring strategies and overflow, outside numbers inside a Crexendo queue, dial by name; the Comcast guide predates a rename. Not borrowed: menus, dial by extension, hold queues, agent presence. Clara is the attendant.
Goes away: the leasing ring list and the older leasing hand-off setting; the two "everything else" settings; the "two modes" of Oct 7; the call platform's built-in transfer for person asks. Maintenance and emergency keep their settings, lists and dial paths.
Nothing turns on soon (Fede, Oct 28). Steps 1 to 4 must replay today's calls unchanged; step 5 and anything callers notice wait for Fede.
Everything older, moved here unchanged, newest first. Click a row to open it.
A law firm called Western Slope and asked for Jay by name. Clara sent the call to the leasing ring list, and after the call she filed the caller as a prospect and armed the leasing follow-up texts. The caller's number was already on file as a vendor. The card was archived and its follow-ups stopped the same day. Nothing was written to AppFolio, because the front-door line has no AppFolio property behind it.
| When (MT) | What happened |
|---|---|
| Oct 14, 12:38 PM | #19632 merged. Any hand-off to the leasing ring list now counted as a reason to file the caller. |
| Oct 15, 2:48 PM | A law-firm caller says "Jay Taylor, please." Clara hands the call to the leasing ring list. Kat answers and passes it to Jay. A privileged legal conversation follows. |
| About 20 s after Clara's part ends | The after-call reader sees only Clara's part, which is one line. The AI says "no topic, not a lead." The leasing hand-off rule overrides it, a card is made, and the follow-up texts are armed. Nothing checked that the number belongs to a vendor. |
| Oct 15, evening | The card was archived and its follow-ups stopped. The harness (#20217) and the fix (#20221) were opened. A gap found on the way: archiving a lead stopped its first follow-up texts but not the "went quiet again" texts for leads who had replied. Fixed in #20231. |
Root cause. #19632 (our own session, 10/14) widened the signal and relied on the AI reason alone, with no deterministic gate and no logging. The rule it widened had already been wrong 5 out of 5 times: every card it made was not a leasing call. The cards were the lawyer, Jay's own ask, a photographer's sales call, a job applicant and a hardware-store vendor.
| # | Who is calling | Result | Proof |
|---|---|---|---|
| 1 | A known vendor | Never a card | Replay of the lawyer and two other vendor calls, plus a planted break that turns the test red |
| 2 | Staff of any role, or a current tenant | Never a card | Jay's own call and a tenant who said "leasing", one planted break each |
| 3 | Someone who already has a card | That card is updated, never a second card | A returning applicant, plus a planted break |
| 4 | Anyone else | A card only if the whole call was about leasing | Noah's call still makes a lead from the person's part. Two planted breaks ("any leasing hand-off makes a card", "read Clara's part only") turn it red |
| Measure | Number |
|---|---|
| Western Slope phone leads Clara made | 32, of which 12 were not leasing (38%) |
| Stopped by this fix | 7 of the 12 (5 hand-off cards, 1 vendor, 1 staff member) |
| Already stopped before it | 2 (a tenant, and an "operator" ask the AI now declines) |
| Still open | 3: two internal test calls and one software sales call. Follow-ups filed. |
| Real Western Slope leads that came only from the hand-off rule | 0 of 18 |
| Cards the AI alone said yes to | 52, of which 8 were not leasing (15%) |
| Full-call judge, alone vs with identity checks first | 16 of 20 right vs 19 of 20 right |
| Camellia, the other direction | 11 real leads on transferred calls got no card |
Fede: "Do I create a guest card? Do I create a tour? Do I send an application?" The after-call step should choose one outcome per call: guest card, tour, application link, team note, or nothing. Identity runs first, as above. The verdict and its reason are logged as ids and labels, never the call's words. The 90-day pull is the test set: 256 Western Slope calls and 134 Camellia transfers, each already labeled with the right outcome. The bar is 98% of real leads caught and 95% of cards correct.
Harness: evals/fast-tier/postcall-prospect-capture/replay/ (13 scrubbed real calls) and src/__tests__/postcall-capture-replay.test.ts; subscription runner scripts/replay-postcall-capture-subscription.ts. Before the fix, 7 of the calls made a wrong card. After, all pass, and all 7 planted breaks go red. Not yet proven on a live Willows call. Incident: #20208.
Decided (Fede, Oct 7) "Take a message" is the default when nothing is set, and onboarding confirms each call type was looked at.
| # | Step | Kind |
|---|---|---|
| 1 | After-call reader runs for every client (delete the test-only gate) | fix, no switch only after the overnight replay passes |
| 2 | "No message" never drops a call we know who + what for | fix, no switch |
| 3 | Screener or voicemail is not a person; ring the next one | fix, no switch proven on a branch (128 transfers replayed, 2 flips, 0 real people); Fede Oct 8: not yet |
| 4 | Property default list for on-site clients, read from the stored mode | your call changes who Camellia and Yale callers reach |
| 5 | No call ever ends in nothing: a failed dial takes a message | fix, no switch |
| 6 | Nightly check: every call ended with a person or a delivered note | fix, no switch |
| 7 | One office-hours store and one "is it open" check | fix, no switch; Saturday to tour hours at Camellia and Yale is your call |
| 8 | Every agent on the one connect step; delete the old button and leftovers; routing onto Gera's lookup | fix, no switch |
| 9 | Retire the live note tool; Clara says "I'll pass that on" | your call after step 1 is proven |
At altitude: two facts carry the rest. First, anything set inside the voice vendor's agent configuration is shared by every client, so it can never differ per client. At dozens of customers with different phone setups, every hand-off has to run through one dial path we own, where who to ring, which number shows and whether a person answered are data, not code. Second, that data is the settings the customer sees on the Calls & messages page, read through Gera's shared lookup, so the page and the engine can never disagree. Everything else is implementation inside those two.
| Pillar | What it means | Ruling |
|---|---|---|
| Clara is the phone system | Staff use their own cell, nothing to install; the whole call is recorded end to end, after pickup and after any transfer. | 9/19 |
| Ring lists are people | An entry is a person with a number, rung in order; Clara is the middleman and never reveals a cell. No key press ever: a person is told from voicemail by listening. | 9/20, 9/23 |
| One hand-off logic, all agents | The same ask gives the same outcome on every agent; one notice builder for every notice; greeting is a setting, routing is one core. | 9/21, 9/22 |
| Two modes, one core | Centralized (company, per call type) or on-site (property default); nearest set level wins; nothing set = take a message; no hidden fallback. | 10/7 |
| Live tools only when the caller needs the answer | Tours, calendar, transfers. Notes, notices, guest cards come from the transcript after the call. One way, no backups. | 10/7 |
| Never infer, always observe | Caller ID is a fact per destination, never guessed from the number; every destination proven with a real call at onboarding; every transfer gets a verdict and every destination shows its results. | 10/8 |
| Emergency chain = same engine | The ring list with a different trigger: an emergency-category work order. Not a second dial path. | 10/8 |
Caller ID lineage: 9/20 open trade-off (caller's number vs Clara's line) → 9/23 caller's number everywhere → 10/8 a choice per destination. Older text in the history below that says "press 1 stays the fallback" or "staff still see the original caller's number" is superseded.
| Gap | Today (proven) | Kind |
|---|---|---|
| Three dial paths | Clara's connect step (2 agents), the ring-list and maintenance-menu dial, and the vendor's built-in transfer on 6 agent configs that can never be per client. | architecture |
| Ten who-to-ring lookups | Gera's shared lookup exists and is used only by the office-hours check. The routing rebuild on it is proven on a branch, same answers at Camellia, Western Slope, Yale. | architecture |
| Eight pickup verdicts that disagree | The ring-list dial advanced correctly past a silenced phone on 9/20; the connect step logged a screening robot as "connected" on 10/8. | architecture |
| Page and engine read different settings | "Talk to a person" reads one key; the engine rang Joanna from another no screen shows; leasing falls to the office phone through a hidden fallback. | architecture |
| Caller ID is one fixed rule | Caller's number everywhere since 9/24; no per-destination fact; carrier acceptance of the incoming line as the shown number not yet proven. | architecture |
| Nobody watches the destination | 7 detectors, none checks the whole call or a destination's track record; Camellia went two weeks unnoticed. | architecture |
| Office hours in two stores plus knowledge text | 5–6 separate "is it open" checks; Camellia's Saturday tour window read as office hours. | architecture |
| Notes depend on a live tool | About 12 note paths; the after-call reader runs only on the test bench; the single after-call path is not yet ready (40 of 71 reproduced, 3 real losses). | architecture |
| Emergency on-call list is dark | Settings card and keys exist; rows only at the test companies; the ring-list engine has no emergency trigger; the chain, timeout and email leg are unbuilt. Gera's lane, contract on the bridge. | architecture |
| Yale 25 Station has no maintenance or emergency destination; Situs has no line | Neither is live; both are onboarding answers, not code. | onboarding data |
Nothing in this section is built or switched on. Each gap closes as a fix with no switch, proven by replay and real calls on our own lines, in the order on the fix list above.
One new line under each person on the Call routing card: Shows: the caller's number (today's behavior, the default) or the line the call came in on. Only PropFlow staff see the select; the customer sees the list as before. Change Joanna's select below and the phone shows what she would see.
Two real calls into the Fairhaven test line, each asking for a person. The call was passed to a PropFlow test phone. We read what that phone showed from Twilio.
| Call | Setting | Test phone showed | Twilio call ID |
|---|---|---|---|
| 1, first leg | Not set | The caller’s number (+17205942061) | CAe6057f3d1097ba872c09b64a6e561b18 |
| 1, second leg | Not set | The caller’s number (+17205942061) | CAb41e1533f87698508747ae48f0d9fa2a |
| 2, first leg | Line | The Fairhaven line (+17208071724) | CA52fdb7476133b7d0f21f701f5c6bc4b5 |
| 2, second leg | Line | The Fairhaven line (+17208071724) | CA39fbf855f2497596c2c60e8c31116ef9 |
Turning the setting on changes what the person’s phone shows, and turning it off puts it back. The setting was cleared afterward.
Turned on at Camellia, Oct 9, 2:04 PM Mountain. Fede’s call: Joanna’s row at JP&Co is set to “the line”. Fede then called the Camellia line from his cell at 2:16 PM and asked for a person; Clara rang Joanna and her phone showed the Camellia line, (844) 510-1007. Twilio shows the leg to Joanna leaving from the Camellia line.
| Leg | From → to | Twilio call ID |
|---|---|---|
| Fede into Camellia | Fede’s cell → (844) 510-1007 | CA33e2ae28bc602725d1eeef197e6cd5c6 |
| Clara to Joanna | (844) 510-1007 → Joanna | CAdb303d3690435abf80462f52f57f6c40 |
Known gap: the app does not show this choice for Camellia today. The control renders only beside people on the company-level ring lists, and JP&Co stores Joanna on the building. Follow-up issue 18329; the redesign (one routing card at both the company and the building level, each row labeled with where it is stored) is paused pending Fede.
The routing change that passes the call’s group to every lookup (the third routing PR) is live in production. We re-proved the Willows test company’s routes with real calls: the proof script resolved each route from the stored settings, then dialed the answer. Every dial reached our test phone. Two placeholder emergency numbers on the test buildings were refused by the script’s own safety list, which is the guard working.
| Route | Resolved from | Reached | Twilio call ID |
|---|---|---|---|
| Maintenance | Company ring list | Test phone, picked up at once | CA74801f8b2bead842ae3ed56d50229cb2 |
| Everything else | Company ring list | Test phone, picked up at once | CA8054034a0c50099f19d2c0a9f9d5e4a9 |
| Emergency | Company emergency route | Test phone, picked up at once | CAa29b0b6d8fa1f85baf0e2e12988d4f8d |
| Talk to a person | Property handoff setting | Test phone, picked up at once | CAafc1c8861c4bac2824292f683fe46add |
| Emergency phone on the property record | Property record | Test phone, picked up at once | CAf5858a3bd3e2f0640c124c57b7432cc0 |
These calls were placed at 2:01 PM Mountain from the code on main that includes the routing change. An earlier set at 1:10 PM ran from a stale checkout without it and was replaced; those five calls (CA903bd4…, CA7f7cad…, CAf1eb74…, CA113d28…, CA1dd74a…) reached the same test phone and stand as the “before”.
Before: the same five answers, resolved without the group step. After: the same five answers through the shared lookup, each one proven by a real call. No customer’s routing changed; the before/after on real customer settings was byte-identical in the PR. One note: the proof script does not look at the clock, so running it after closing time would dial the same five numbers and prove nothing new. The after-hours message desk is covered by its own eval scenarios, not by this script. The Fairhaven test company could not be used for this run because one of its buildings is missing the test flag. That building (“Company line bench building (live)”) is not created by any code in the repo, so we left its record alone for whoever owns it; the Willows test company covers the same routes.
Read from the raw per-speaker transcripts of nine client calls: Western Slope (9/4, 9/9, 9/16, 9/22, 9/30, 10/7), MMG Denver (9/18), ConAm/Yale (9/29, 10/1). No JP&Co or Situs call in the window. Asks we already meet are left out (forward the main line, ring a list then take a message, written summary with transcript and recording, staff texting Clara, no duplicate guest cards).
| Ask | Who | Where we are |
|---|---|---|
| Emergency chain: call, text and email the on-call person at once, 5-minute timeout, keep trying until someone acknowledges. Issue types from the work order (AC, heat, water, gas, habitability). | Jay 10/7, Jason 9/30, Hugo (MMG) 9/18 | Not built. Gera's lane. The two Western Slope calls disagree on who is first (Cheryl on 9/30; maintenance manager, then Jay on 10/7); confirm with them before building. |
| Escalations go to Kat by text, not the shared leasing inbox ("it's gonna get drowned"). | Jason 9/22 | Building (Clara as coworker on Kat's channel). |
| Retire the AppFolio after-hours number printed on 220 magnets, or forward it to the main line. | Jay 10/7 | Nothing planned. |
| After-call summaries into a ticket or shared inbox, not an email distribution group. | Jay 10/7 | Email to the shared inbox today; no ticket option. |
| Police calls go to Cheryl; "if this is an emergency, call 911, we're not the police." | Jason 9/30 | No rule for law enforcement or 911. |
| Ask each staff person how they want to be reached (text, email, call). | Jay 10/7 | Not built; every notice goes the same way. |
| MMG: one main number for all properties, residential vs commercial routing, commercial after-hours. | Hugo 9/18 | Not supported (one number serving many properties). |
Two signals. Nobody asked about caller ID, but Jay saved the after-hours number "so I know it's an emergency", which is the per-destination setting above. Nobody wants a key press to accept a call, but staff do expect to acknowledge an emergency, so the emergency chain needs a defined "got it" step that is not press-1.
Superseded 2026-10-28 by the design at the top of the page: the "two modes, one core" shape below is replaced by the one-engine design (one shape for every customer). Kept for history.
Correction to the Oct 7 inspection (in History): a per-property "everything else" list does exist (today's Camellia fix used it). Missing is the rule making it the default for every topic in on-site mode.
Caller ID in the field (Oct 8, sources in history): Twilio shows the caller's number by default; Smith.ai, Google Voice, Dialpad and Retell each offer one setting (their line or the caller's) per account, device or transfer step; EliseAI and RingCentral don't document it; Apple's answer to a silenced personal phone is "save the number as a contact".
| Setting | Levels: today → target | Empty today | Empty under the design |
|---|---|---|---|
| Client mode | company (stored, routing ignores it) → company, routing reads it | — | centralized |
| Property default (on-site) | doesn't exist as a default → property | — | fall to the call-type list, else message |
| Leasing | property, company (group skipped) → all three | blind dial to the office phone on one agent; message on another | message |
| Maintenance | property, company (group skipped) → all three | a failed direct dial ends the call | message |
| Emergency | property phone field only → all three, a chain of people | nobody | message + flagged missing at onboarding |
| Everything else | people list all three, office line company-only → one list, all three | message, if the AI remembers | message |
| Named person | doesn't exist → staff records | nobody | message for that person |
| Office hours | 2 stores, property wins → company, property only on the client's word | company hours | company hours |
| Note recipient | company shared inbox → same | no email (Situs) | flagged missing at onboarding |
"All three" = property, group, company; nearest set wins. The group level is never read on a call today.
| Client | Leasing | Maintenance | Emergency | Everything else | Named person | |
|---|---|---|---|---|---|---|
| Camellia on-site, live | Today | depends on the agent: message, or blind dial to the office line | Clara's maintenance agent | property emergency number | Joanna in hours (set today, unproven); message after | nobody |
| Design | property default (Joanna) for every topic in hours, through the chain with the screening check; emergency chain any hour; message otherwise | |||||
| Missing | proof on a test line shaped like Camellia's; screening check; an emergency chain | |||||
| Western Slope centralized, live | Today | Kat, then Bev (20 s) | outside call center | none on Clara (the call center covers it, inferred) | message, by Jay's choice, if the AI remembers | nobody |
| Design | same, at company | Clara, from Friday | maintenance manager → Jay → technician | message, no ring | message | |
| Missing | emergency chain at company; every note to the shared leasing inbox with suggested category, matched AppFolio record, transcript and recording link (no test pins the recording link today) | |||||
| Yale 25 Station on-site, not live | Today | office phone, by accident | nobody | nobody | nobody | nobody |
| Design | property default for every topic once set; message for everything until then | |||||
| Missing | property default, emergency chain, maintenance destination; Saturday out of office hours | |||||
| Situs centralized, no line | Today | no phone line, no team email | ||||
| Design | company destinations per call type once a line exists; everything is missing today | |||||
System design (Oct 8): "one way to hand off a call" is a decision on the Clara platform vision page, beside "one picture / one door". This page stays the owner of the call-type routing design, the fix list and the proofs.
Read-only, Oct 7 evening: no calls, texts or emails. Times Mountain. "Inferred" = not proven live.
Inputs: Fede's rulings, 10/7
Each stage is done several ways, each choosing its own levels. Breaks on the right are real incidents since Sep 25.
| # | Root problem | Evidence, in plain words | Incident it caused |
|---|---|---|---|
| 1 | The note depends on the live AI | The AI must remember to call a slow tool mid-call. In 15 of 15 non-leasing misses it never fired. Every after-call safety net is test-only, retired, log-only or very narrow. | 19 promised notes not sent; the Camellia unit-416 applicant (5 calls) |
| 2 | Routing is not one core and ignores the client's mode | ~10 lookups, 2 engines. The client's mode is already stored (on-site at JP&Co, centralized at Western Slope); routing never reads it. | Camellia rang nobody for 12 days after the Sep 25 connect change |
| 3 | "Connected" is not checked, and a dial can end in nothing | "Connected" means three things: the phone company accepted the redirect, the machine check said "not a fax", or a voice was heard. A screener and a greeting are voices. | Sep 29 screener counted as connected |
| 4 | Test-only gates | ~9 behaviors run only on one sandbox. The main test lines can't reach them, so "proven on test" proves nothing about clients. | Connect change "proven" on a test property set up unlike any client |
| 5 | Office hours have no single home | 2 stores plus knowledge-base text; 5–6 "is it open" checks. Property data overrides the company card at both live clients, so card edits do nothing there. | Clara told Camellia callers the office is open Saturday (9/29, 10/1) |
| 6 | Nothing watches the whole call | No check that every real call ended with a person or a delivered note. | 12 days of "nobody to connect" logged as ordinary info |
The section above's "8 of 15 reached nobody" is Camellia only; 19 here is Camellia plus Western Slope.
Moved to the top of the page (2026-10-07 restructure): the target design diagram, the "every setting" table and the per-client table are under The design.
Moved to the top of the page (2026-10-07 restructure): the 9-step fix list is under Decisions for you; the overnight box is under Running overnight.
Inspected 2026-10-07. All times Mountain. Facts are verified unless marked "inferred".
Centralized clients attach destinations to the organization; on-site clients attach them to the property. That is the same org-or-property attachment Gera's portfolio architecture defines, so nothing new is being invented there.
| Client | Mode | Leasing person-ask | Everything else | Maintenance | Emergency | Hang-up note | On the new model? |
|---|---|---|---|---|---|---|---|
| Camellia | on-site | office line (on-site manager) in hours; in-hours leasing connect never yet exercised live | fixed today 11:38 AM property list rings the on-site manager in hours, message after hours | no destination | legacy emergency phone | off | partly office line and legacy emergency still carry traffic |
| Yale 25 Station | on-site, not live yet | office line | none | module off | none | off | no old paths only |
| Western Slope | centralized | two leasing staff in order, 20 s each (old-style setting) | none, by choice Jay, 10/7: note to leasing@ instead | external call center | none on 90 properties | off | partly leasing uses the legacy handoff setting |
| Situs | not routable | none | none | none | none | off; no team email at all | no nothing configured |
| Test org | — | set | set | set | set | on in one sandbox only | yes which is why tests passed |
No client is fully on the new model. Only the triage and unknown-caller agents use the new connect step; five other agents still carry the old transfer button with blank legs. JP&Co's office hours (Mon–Fri 9–5, weekends closed) stand, matching Camellia's website: Fede decided 10/07 that calls respect the configured office hours and nobody is rung on Saturdays.
| # | Broken | Plain proof |
|---|---|---|
| 1 | "Everything else" connects to nobody at every live client since Sep 25. | Camellia: 12 connect attempts, 0 connected (11 "everything else", 1 leasing after hours). An approved applicant for unit 416 called 5 times, Sep 29–Oct 7. A JP&Co staff member asked for management Oct 6, 11:59 AM, in hours; nobody rang. |
| 2 | Messages silently lost. Of 15 Camellia calls since Sep 25 where a person or message was asked for or promised: 7 notes saved, 5 emails confirmed delivered (1 more probable), 8 reached nobody. | Five leaks: nobody to connect; caller hangs up or gives no reason, so no note is saved; the team-side backup for unkept promises was retired (inferred: it went with the 9/29 change that stopped promise texts to callers); since Oct 4 the notice system drops any call with no message, even when we know who called and why; the hang-up safety net is on in one sandbox only, dark 28 days, no proof. |
| 3 | Pickup check cannot tell a screening prompt from a person. | Sep 29: an older agent transferred to the office line, hit "record your name… stay on the line", and was recorded as "connected". |
| 4 | Clara improvises when there is nobody to connect. | Oct 1, 12:14 PM, in hours: Clara said "the office is closed" while the call data said open. Inferred: the model filled the gap. Office hours parse fine everywhere; one bad day voiding the week is a weakness, not the cause. |
| 5 | Wrong language on email. | The applicant emailed Oct 7, 10:44 AM; Clara replied in Spanish because the website form tagged her as Spanish. Email skips the written-language check. Another session owns the greeting/footer side. |
| 6 | Clara tells callers the office is open Saturday. Tour hours and office hours got mixed. | Camellia's stored property knowledge lists Saturday 1–4:30 as office hours; Clara quoted it on Sep 29 and Oct 1. That window is the weekend tour slot, by appointment only, typed in by hand around May 10–16 when the Camellia team announced it; it went into the only hours field that existed then (the row keeps no history, so the writer cannot be named). Fede flagged the mix-up on Sep 16 and Sep 20; the fix was planned and never done. Yale 25 Station has the same problem (Saturday 9–4 in office hours since its March seed, no tour hours set). See History below. The website says Mon–Fri 9–5, and so does the company setting. Hours live in two places and disagree; the decided design already says office hours are their own setting, not tour availability. |
| 7 | Her record is stale and split. | Our record says "application pending" while AppFolio shows a signed lease starting Nov 1 (sync lane). She exists as two person records (identity lane). |
| — | Blast radius at other clients: not counted yet | The count stopped before it finished. Rerun pending Fede's yes. |
No cross-client leak: an Oct 1 "missed call" email to Western Slope was a Western Slope call. Recipients are always read from the call's own property or organization.
Fede: "we want every client and customer in the same new architecture." The decided design (one routing core, people lists, no keypress, caller's own number, message with full context, one notice builder, proven destinations at onboarding) still lacks five things:
| Gap | What it means |
|---|---|
| Per-client mode | A client is either centralized or on-site. Decides where destinations are read from. Does not exist. |
| Property default list | One list per property that covers every topic unless a category overrides it. Does not exist; "everything else" is company-wide only. |
| Migration plan for existing clients | None written. Camellia was explicitly left on single-number forwarding pending a rehearsal; the old paths (office line, legacy emergency phone, legacy leasing setting, property hours shadowing company hours, old transfer button on five agents) still carry live traffic. |
| Monitoring | Nothing alerts when a route ends with nobody. Engineering alerts are being built now (see fix order). |
| One home for office hours | Fede, 10/07: "the website should not be mutating company settings." Office hours live in the company setting, with a property override only where the client says so. Tour availability stays a separate setting. The website tool is for concessions only and never writes a setting Clara acts on; the code that could overwrite office hours from the website is dead today and should be deleted so it cannot be revived. |
| The bar: never strand | Every route, at every client, in every mode, ends with a person on the line or a saved message that reaches the team. A route that can end anywhere else is a bug, not a setting. |
Clara stays the middleman on every leg. The directory is the staff records we already keep; nothing is typed twice.
Open questions, with a recommendation:
The 10-06 page already proposes one harness: a grid of company × conversation kind × channel × situation, run on the real decision code with nothing live, red first with planted-wrong controls. This folds into that grid; no second plan.
Fede has approved none of these yet. Decided already: the two operating modes, calls respect configured office hours, and the website tool limited to concessions.
History of the office-hours mix-up
Doc drift: this page's older caller-ID and pickup-mode entries predate the 9/23 rulings (original caller number shown everywhere; no keypress ever). Read those two rulings as current.
| Mechanism | Caller ID | Destinations | Whisper | Pickup detection | Setup |
|---|---|---|---|---|---|
| ElevenLabs blind transfer | Preserves original | One | None | None | Native Twilio numbers only, JSON editor only (docs) |
| ElevenLabs conference transfer | Not documented | One, no ring group | Yes (agent_message) | None | docs |
Twilio <Dial> from the inbound call | Passthrough by default | sequential, up to 10 | Per-number url | machineDetection, action fallback | Dial / Number |
Chosen combination: blind transfer for single outside lines. For the staff ring list, our hunt step pushes TwiML <Dial callerId={From} sequential> onto the live call — Twilio's own published ElevenLabs handoff pattern (Twilio blog).
Must be proven by a real call:
url and machineDetection coexist on the same <Number>, and in what order.<Dial> forward carries the inbound SHAKEN PASSporT onward.Risks: STIR/SHAKEN attestation C on passed-through numbers (spam-label exposure on staff cells); error 21210 if the binding is lost (retry once from the business number); CallToken TTL undocumented; the first endpoint that connects swallows a sequential hunt.
Status: Proof in progress via the voice fast lane; PR #11075 + hunt-route PR; merge = deploy to every production agent, on Fede's yes.
These two pictures are the whole page in miniature — what a call does today, and where the design is headed. Everything below is the backup for these two pictures.
One fixed menu, three lanes. The team finds out afterward — by email, transcript and recording.
One greeting setting, one classifier, one routing layer. Menu or open question, the routing underneath never changes.
| What | Today | Future | Status |
|---|---|---|---|
| The greeting | Fixed 3-option menu: leasing, maintenance, or something else | Per-client setting: keep the menu, or switch to an open “how can I help you today?” | Proposed |
| Deciding what the call is about | The caller's menu pick decides everything downstream | One intent classifier decides; a menu pick is just a hint to it | Proposed |
| Reaching a specific person by name | Not possible — the caller has to guess a lane and hope | “Connect me to Jay” matches the person by name or role directly | Proposed, pending client OK |
| Caller's number on a call-center forward | The call center always sees Clara's number, never the caller's | Can show the real caller's number instead, per client, using a Twilio feature | Proposed — proof running |
| “Something else” callers | Go to an empty catch-all list today — Clara just takes a message | Goes straight to the property manager (or the catch-all list, once a client fills it in) | Proposed, confirming with client |
| Who staff calls come from | Always Clara's line, never a raw cell | Same — unchanged | Decided |
Every forward — a staff ring list, an external forward to a call center, or the catch-all office line — presents Clara's own line as the caller ID at the destination. Staff and outside destinations never see the actual caller's number on their phone screen.
That is a deliberate choice for staff (below). It is an accident for everyone else, and it broke a real call on 9/22.
What happened at Western Slope: a tenant called about maintenance and was forwarded to the client's outside maintenance call center. That center's own phone tree offers "press 1 to get a text." The tenant pressed 1. The text went to Clara's line, not the tenant's phone — because Clara's line was the caller ID the call center's system saw. Conversation conv_voice_47fe63f4, SMS thread 5d06dcb1, written up in PR #10775's body. Fede, seeing it: "I thought we fixed this."
What was "fixed" on 9/20 was a different problem. That ruling is about staff handoffs — the ring list that calls Kat, Beverly, Gera, and so on. Fede's reasoning there: Clara is the middleman, staff never see caller cells and callers never see staff cells, so staff save one number once (Clara's) and a call from it always means a real handoff. That reasoning does not apply to an external call center's own phone tree, which is not a person and does not get "Clara is the middleman" — it just needs the real number to text back.
PR #10775 shipped a per-org voice.forward_caller_id setting ('caller' vs 'clara_line') in its first round. Round-2 review proved the premise wrong and the 'caller' mode, the setting, and its helper files were deleted in the same PR before merge:
Twilio only lets a forwarded call present the caller's own number when that number is the current call's own inbound "From." Both places PropFlow dials out from — the ring-list leg and the external-forward leg — are calls placed from the property's own line, not from the tenant. That's proven live: Twilio call CA16cded30f4fffbb359d16bf988c1cf33, documented in the route's own header at hunt/route.ts:49-56. Presenting the tenant's number there is presenting a number the account doesn't own on that leg — Twilio's <Dial> rejects it with error 13214. A reviewing bot caught this before it shipped, citing the same line numbers and a live call SID (CA429a4a046a720585bcf7be5dc0c5b648 / child CAace5f915729ae1c3901e06676b18d36a).
PR #10775, still open, says the real fix plainly: "A future redesign that dials the destination from the ORIGINAL inbound leg (where the exemption would actually apply) can reintroduce a caller-ID setting when it's real." That's Option A below. Follow-up issues #10787–#10791 track the leftover pieces.
The 9/22 proof will show whether the call token lifts this restriction on the transfer leg; if it does, the deleted setting comes back in this form.
Clara runs on ElevenLabs' native Twilio integration, not a SIP trunk: the inbound call registers with ElevenLabs (elevenlabs-client.ts:97-155) and ElevenLabs holds that leg directly. When Clara transfers, its transfer_to_number tool places a new, separate outbound call from the property line into PropFlow's own hunt/forward logic (hunt/route.ts) — it does not hand over the caller's original inbound leg. That new call's own "From" is the property line, which is why callerId: to (the property line) is what Twilio will actually accept there (hunt/route.ts:851-857, hunt-chain.ts:306-312).
| Leg | Whose call is it | Where it's held |
|---|---|---|
| Caller's original inbound call | Tenant → property's public number | ElevenLabs' native Twilio integration (media stream), CallSid stored on the conversation (types.ts:10223) |
| Transfer leg | Property line → PropFlow's hunt route | hunt/route.ts — this is the leg whose "From" everything downstream (ticket lookup, AMD, recording) keys off today |
| Destination leg | PropFlow → staff cell / call center / office line | Twilio <Dial>, callerId currently forced to the property line |
The caller's original inbound CallSid is already known and stored — it lands on the conversation record at transfer time (personalization/route.ts:347, saved incoming/route.ts:237). So the number Option A needs is there. What's missing: (1) Twilio's call-update helper in this codebase today only tears down a call (client.ts:182) — nothing redirects a live call to new instructions yet; and (2) the hunt route resolves which caller a ring ticket belongs to by matching "From" to the property line (hunt/route.ts:643-673) — a redirected inbound leg would arrive carrying the tenant's own number as "From," which breaks that lookup as built. Both are buildable; neither is built.
| Option | How it works | What the destination sees | Caller experience | Twilio rule it relies on | Effort | Risk |
|---|---|---|---|---|---|---|
| A0. Twilio call token on the existing dial Decided | Pass the inbound leg's CallToken and callerId=caller on the outbound <Dial>/Calls.create from the transfer leg — no new call flow, no leg redirect. | The tenant's real number | No change | Twilio call-forwarding caller-ID changelog | S if the proof passes | Low — no new call flow |
| A. Redirect the caller's own inbound call Fallback if the token proof fails | At transfer time, update the caller's original Twilio call (not the transfer leg) with new instructions: Calls(inboundSid).update(twiml=<Dial callerId={callerNumber}>…). The destination is dialed from the leg where the caller's own number actually is the "From." | The tenant's real number | No new hold; the call already leaves Clara at transfer today. A brief instruction swap, not a second call. | Caller-ID pass-through applies to a call's own current inbound "From" (Call resource; Twilio's call-forwarding caller-ID changelog, which describes a callToken mechanism for exactly this) | L — needs a live-call-update helper (doesn't exist yet), a stored inbound CallSid on the hunt ticket, and reworking ticket lookup so it doesn't require this leg's "From" to be the property line | Medium — unproven on the wire; touches recording, AMD, and ticket resolution, all of which currently assume the property line is "From" |
| B. Pass the number another way, keep Clara's line as caller ID | Whisper the number before bridging ("caller is 970‑xxx‑xxxx, connecting"); SIP custom headers / UUI if the destination is SIP; or sendDigits to type the caller's number into the destination's own IVR digit-by-digit | Still Clara's line, but a human or IVR receives the tenant's number by voice or by digits | No change | None of these change callerId — whisper and sendDigits are plain <Dial> features; SIP headers need the destination to run SIP, which Western Slope's call center likely doesn't | S (whisper, sendDigits) / M (SIP headers, needs destination support) | Low, but narrow: whisper only helps a human who answers live, not an automated "press 1" flow like Western Slope's. sendDigits is the one piece of B worth testing against that specific IVR this week. |
| C. Text the tenant's number to the call center after transfer | Send an SMS with the tenant's number to whatever inbox the call center reads | Depends entirely on whether that center accepts and reads inbound SMS to a number Clara can reach | No change | Not a caller-ID mechanism at all — ordinary SMS | S | Untested whether Western Slope's center accepts this; doesn't fix the "press 1 to text" flow itself, which is the actual thing that broke |
| D. Dedicated per-client forwarding number, CNAM'd | Give the forward its own Twilio number with CNAM set to the client's name ("Western Slope") | Not the caller's number — a labeled business name instead of "Clara" or "Unknown" | No change | CNAM: 15-character limit, US landlines by default, mobile opt-in only, 48–72 hours to propagate to carriers | S | Low, but partial: fixes confusion about who's calling, does not fix the wrong-number text |
Also checked and ruled out for this problem: pre-verifying every tenant's cell as a Twilio Verified Caller ID is a one-time, human-in-the-loop setup per number (answer a call, read back a code) — not workable per-call for tenants who call once. SHAKEN/STIR attestation (A/B/C) is a separate carrier-trust signal, not something we set directly; a forwarded call whose caller ID doesn't match its real origin tends to land at a lower attestation level and reads as more spam-like to carriers — another reason Option A (real exemption, real match) beats spoofing the number outright. A2P 10DLC registration governs SMS/MMS trust only; it has no bearing on voice caller ID.
Comparison of how Retell AI, Vapi, Bland AI, Synthflow, and Twilio document caller ID behavior when transferring or forwarding calls to humans.
| Platform | Mechanism | Customer setting? | Limitation | Docs URL |
|---|---|---|---|---|
| Retell AI | SIP DIAL (warm: from/P-Asserted-Identity headers) or SIP REFER (cold) | Yes: "Displayed Caller ID" (Agent's Number vs. User's Number) | Retell Twilio numbers support both modes; Retell Telnyx supports cold transfer (SIP REFER) only. Fails if provider doesn't support caller ID override. | Transfer call docs |
| Vapi | Not documented | Not found | Requires SIP provider support for custom caller ID. Documentation does not address caller ID behavior explicitly. | Transfer call tool |
| Bland AI | Proxy agent dials the human (SIP DIAL inferred); configurable caller ID on proxy call | Yes: "Proxy Agent Number" setting (what number the AI calls the agent from) | Must be E.164 format and from the same Twilio account as the original call. If not specified, system auto-selects from available numbers. | Warm transfer docs |
| Synthflow | SIP REFER (cold: passes original) or SIP DIAL (warm: new leg, default shows agent number) | Yes: "Caller ID" setting with three options (Agent's number, Caller's original number, Custom number) | Original caller ID on warm transfers only works with Synthflow-purchased numbers. Custom caller ID requires provider support. Twilio bring-your-own accounts need "allow any caller ID" capability (not available on most accounts). | Call transfers docs |
Twilio (native <Dial>) | callerId parameter on <Dial> verb | Yes: specify callerId (any valid E.164) | For inbound SIP calls: "callerId cannot be the To or From number, as there is a risk of caller ID spoofing." Must be a Twilio-owned number or verified number. For the caller's own inbound number to show on a dialed leg, that leg must be redirected from the inbound call itself, not a new outbound call. | TwiML Dial documentation |
Twilio callerId exemption, exact wording: "In the case of an inbound SIP call, callerId cannot be the To or From number, as there is a risk of caller ID spoofing." This restriction applies to new outbound call legs. The Twilio call-forwarding caller-ID changelog describes a callToken mechanism for re-directing the caller's own inbound leg to a new destination, which would allow that leg's "From" to be used as the callerId without violating this rule.
sendDigits as a stopgap this week while A is built.sendDigits — that's a day of work and directly un-breaks Western Slope's "press 1 to text" flow without waiting on the leg redesign.Real calls between our own lines only, before anything touches a customer or a personal cell: Clara test line +1 720 807 1724, robot +1 720 594 2061, bench +1 970 715 6952. Show the destination's actual caller-ID display for each option, screenshotted or logged. Then a Willows rehearsal. Then Fede's yes, per customer, before it goes live anywhere real — same ladder as every other change on this page.
How a call to a Western Slope‑style client works today, end to end.
auto (Twilio answering-machine detection, no keypress; a voicemail greeting is not a pickup, the chain moves on) — corrected 9/22 after Kat's cell took a voicemail and Beverly never rang.| Gap | What happens today | Status |
|---|---|---|
| Caller ID on call-center forwards | Center sees Clara's line; a "press 1 to text" reply goes to Clara, not the caller | Decided, proven on our lines; not built |
| One hunt chain per line | Only leasing and maintenance have ring lists; can't ring different people for different topics on the same call in one pass | By design today, not a bug |
| Customer-forwarded main line as "something else" destination | Rings back into Clara and drops the caller (Western Slope 9/22, three real calls bounced) | Fixed in the routing rule 9/22: a customer-forwarded number is never a valid transfer destination; take a message instead |
| "Leasing / maintenance / something else" forces a guess | An AppFolio screening rep asking for "Jay Taylor" had to pick a bucket and picked leasing, landing on Kat's voicemail instead of Jay | Named by Sean 9/22, addressed below with name/role matching |
| Multiple property managers on "something else" | Only one PM destination supported; no fallback order if they don't pick up | Open, named by Fede 9/22 (Situs Group may need it) |
Source: Slack #transcripts, thread starting 1790089098.984989 (Sep 22, ~1–2 PM MT), live call notes with Fede, Sean and Gera. Quotes are verbatim from the transcript; anything not a direct quote is marked inference.
The shift in framing: stop thinking about the phone line as an old-school three-way menu (leasing / maintenance / something else) and move toward Clara understanding the call's real intent and routing on that, the way Camellia already does with an open "how can I help you" instead of a menu. Sean: the trigger for this was a caller (AppFolio's screening rep, asking for Jay Taylor) who had to guess a bucket and got misrouted. Fede agrees with the goal but is wary of complexity: "will greatly increase complexity on the phone line" and "we should take our time and do it right so we don't regress."
| Sean | "I think we should have the phone open with 'thanks for calling Western Slope, how can I help you today'… I think the call routing will work better if we make it more of an open question so that the caller doesn't have to guess if it's leasing, maintenance, or something else… kind of like what we're doing at Camellia." |
| Fede | "I don't love that whole leasing/maintenance/something else because that's like an old school phone system… it just gets really complicated because there's a lot of variation of what people are calling about… we would need a lot of data before we get to that." |
| Sean | "If someone says they want to lease an apartment or they have an issue with their unit, our system will recognize whether that goes down the leasing or maintenance workflow. If it's not one of those two, it's something else and just automatically forward[s] to the property manager… that kind of plays into our sales pitch — you can shorten your tech stack and save money with us." |
| Fede | "I like your simplified mental model… I agree with pitching kind of like replacing the phone line, because they might be able to reduce their bill… I think we could simplify: if it's 'something else' and it's business hours, we always call the property manager." |
Fede's ruling, 9/22 ~3:10 PM MT. One voice agent, one intent classifier, one routing layer for every customer — never two architectures or two voice agents.
| Mode | What the caller hears | What the classifier gets | What's different downstream |
|---|---|---|---|
| Menu greeting | "Leasing, maintenance, or something else?" | Caller's answer, as a hint | Nothing |
| Open greeting (default) | "How can I help you today?" | Caller's own words | Nothing |
Why: the three-bucket menu "is like an old school phone system" (Fede, on the 9/22 call), but the team wants more real call volume before dropping it for everyone. Making the greeting a setting lets each client move from the menu to open-ended at their own pace, with zero change to routing underneath.
Nothing built yet, no PR. When built, this ships as a per-organization setting; the default for every existing client is whatever greeting they already have today.
Each row is one piece of the vision above that's buildable with what exists today, roughly ranked by effort. Recommendation only — nothing here ships without Fede's go, per the standard rollout ladder.
| Item | What it unlocks | Depends on | Effort | Recommendation |
|---|---|---|---|---|
| "Something else" always forwards to the property manager (no leasing guess) | Fixes the exact Jay Taylor / AppFolio-screener miss from 9/22; matches Sean's simplified model | Existing PM destination field; needs the fallback-order case if a client has >1 PM (Situs Group) | S — part-day | Build now — single-PM case is a small change; note the multi-PM gap as a follow-up, don't block on it |
| Name/role matching ("connect me to Jay" / "connect me to leasing") | Lets a caller reach a specific person or role directly instead of guessing a bucket | Ring-list person records already carry name + role; needs an intent-extraction step in the call prompt plus consent language | S/M — Fede estimated "a day or less" to build and test | Build now — cheap, directly requested by Fede this session, low regression risk since it only adds a new way to reach ring-list people that already exist |
| Caller-ID redirect on forwards (Option A, above) | Fixes the call-center "press 1 to text" misfire; caller's real number shows on any forward, not just Clara's line | New live-call-update helper (doesn't exist), moving ticket lookup off "From = property line," re-proving recording/AMD against the new leg shape | L — needs a redesign of how the transfer leg is built, touches recording/AMD/ticket resolution | Build now, but scoped as its own PR chain — already scored "recommended" above; not truly low-hanging (L effort) but highest-pain open bug on the page |
| Open-ended "how can I help you" intent routing (replacing the 3-bucket menu) | The actual long-term vision: Clara reads intent instead of a caller guessing a category | More real call volume to tune against (Fede: routing accuracy is the risk); a broader intent taxonomy than today's 3 buckets | L — open research + iteration, not a fixed scope | Later — Fede's own call: wants more data first, ship the two items above before touching the menu itself |
| Multiple PropFlow-user destinations for "something else" with fallback order | Handles clients with more than one property manager (e.g. Situs Group) | Same ring-list-person infrastructure as leasing/maintenance, reused for a PM list | S — reuses existing ring-list code path | Later, on demand — build when a client actually needs it (Situs Group onboarding is the likely trigger) |
| Replace the client's phone system entirely (staff numbers/extensions inside PropFlow) | The stated end state: client drops or downgrades RingCentral, becomes a sales pitch ("shorten your tech stack") | Everything above proven first; a much bigger scope (extensions, voicemail, possibly outbound dialing UI for staff) | XL — multi-week, not scoped | Never build blind — real direction, but needs its own design doc and a customer who wants to actually cut RingCentral before scoping it |
The problem in one line. "Leasing / maintenance / something else" is too narrow — real calls include emergencies, timely-but-not-urgent things, move-ins, balances, renewals, vendors, verification desks and people asking for a person by name. This section proposes a wider topic list, one routing table, and a ladder that promotes a topic from "a human handles it" to "Clara handles it" only when it is genuinely bulletproof. Nothing here is built and nothing changes today.
Read-only pull from production, 2026-09-22. Western Slope = 40 voice calls in 30 days across 70 properties; Camellia/JP&Co = 658 voice calls over 5 months (May 1 – Sep 22). Willows (1,394 calls) is our test property and is excluded. Dead air is the single biggest bucket — 317 of Camellia's 658 calls and 9 of Western Slope's 40 had no caller speech at all. The table below counts only calls where someone actually said something: 341 at Camellia, 31 at Western Slope. Where the stored topic was the catch-all "none", 51 Camellia calls and all 13 Western Slope calls were read and labelled by hand.
| Topic | Sub-topics seen in real calls | Camellia (5 mo) | Western Slope (30 d) |
|---|---|---|---|
| Leasing | availability, pricing, tour booking, application status | 91 — 27% | 17 — 55% |
| Vendor / contractor | on site now, needs access, job status, invoice, market-data callers | 73 — 21% | 3 — 10% |
| Renewals | offer received, want to renew, want to go month-to-month | 44 — 13% | 0 |
| Resident services | parking, amenities, packages, neighbor and noise complaints | 31 — 9% | 1 — 3% |
| Maintenance request (routine) | plumbing, HVAC, appliance, electrical | 21 — 6% | 6 — 19% |
| Rent / balance / payment | what do I owe, how do I pay, a charge I don't recognise | 17 — 5% | 0 |
| Ask for a person by name new | "can I talk to Jay" — no topic given at all | 22 — 6% | 0 |
| Sales / spam / robocall | fake "business listing" verification scripts | 11 — 3% | 0 |
| Move-out / turnover | notice to vacate, deposit, inspection | 7 — 2% | 0 |
| Verification desk new | employment, income or rental-history checks by a lender or screener | not labelled | 3 — 10% |
| Owner / investor | would you manage my home; owner statement questions | 0 | 1 — 3% |
| Move-in | key pickup, utilities, move-in date | seen but not yet a stored topic (the 9/22 key-pickup call) | |
| Emergency / urgent-not-emergency / collections / lease questions / authority | flood, fire, no heat, lockout; police with a warrant; late rent; pets and lease terms | no stored topic exists for any of these — they land in the catch-all today | |
| No content | caller never spoke, or hung up before speaking | 317 of 658 calls | 9 of 40 calls |
What the numbers say. (1) Leasing is our biggest real bucket and it is growing at Camellia — 10% of volume in May, 22% in September. (2) Vendors are the surprise: a fifth of Camellia's real calls, and the current menu has no bucket for them. (3) Maintenance is only 6% at Camellia because their calls go to a call centre — at Western Slope, where Clara takes them, it is 19%. (4) Renewals are lumpy, not seasonal: they cluster on lease-expiry dates. (5) Half of everything that reaches us is silence; any topic model must have a clean "nothing to act on" exit.
| Source | What it says | Quality |
|---|---|---|
| Hemlane maintenance data | Plumbing 25%, HVAC 12%, appliance 12%, electrical 8% — four categories are ~70% of maintenance volume; 3.3 requests per unit per year | Vendor data, 193k work orders |
| AppFolio renter preferences 2026 | 66% of residents pick the phone for an emergency; 51% email and 43% text for anything routine | Survey, n not published |
| Buildium emergency guide | Emergency = gas, fire or smoke, burst pipe or sewer backup, total loss of power, no heat or AC in extreme weather, broken exterior lock, structural damage. Everything else is next business day | Vendor help doc |
| Property Meld triage | Three tiers with response clocks — emergency 1 hour, urgent 24 hours, routine scheduled. "Not every after-hours contact is an emergency" | Vendor help doc |
| EliseAI escalation behaviour | Buckets: leasing, payments, renewals, maintenance, concierge. On an emergency the AI tells the resident it is transferring, then calls the on-call technician while the resident holds | Vendor help doc |
| Vapi PM template / Bland / Retell | All three verify caller type (tenant vs prospect vs vendor) before routing on topic, and warm-transfer with the full transcript attached | Vendor docs and templates |
Where we differ from the industry picture. Every vendor taxonomy is built around maintenance and leasing. Ours is too — but vendors calling in (21% at Camellia) and verification desks (10% of Western Slope's real calls) appear in no published taxonomy we could find, and both are the exact calls that misroute today. Nobody publishes a real call-mix benchmark, so our own numbers are the only honest baseline we have. RealPage Knock and Zuma both hand off on the same rule — if the answer is not in the knowledge base, roll to a person rather than guess (Zuma, RealPage); heating emergencies spike ~300% in December–January and leasing peaks May–August at month-end (seasonality, leasing season). The one thing everyone agrees on and we do not do yet: identify who is calling before routing on what they said.
Identity beats the stated topic. A tenant who says "leasing" is asking a renewal or lease question, never a prospect — they never get a guest card. A caller on a vendor's number who says "maintenance" is a vendor, not a work-order intake. Identity is resolved first, then topic, then the module switch.
There are exactly four outcomes. Clara handles it end to end. Transfer to a person — a role's ring list. Forward — the client's call centre, emergency line, or office line. Take a message — full context, emailed to the team.
| Topic | Identity needed | Module that owns it | Module ON and bulletproof | Module OFF or not bulletproof | Office hours vs after | Who gets the notice |
|---|---|---|---|---|---|---|
| Emergency | anyone | Emergency (checked first, before every other rule) | Forward to the emergency on-call list, any hour | Forward to the client's emergency or main line; if neither, ring maintenance on-call | Same both ways | Maintenance on-call + org admin, immediately |
| Urgent, not an emergency | anyone | Catch-all | Transfer to the property manager | Transfer to the property manager | Hours: transfer. After: message | Property manager |
| Maintenance request | tenant | Maintenance | Clara takes the work order into the PMS | Forward to the client's call centre or maintenance line | Same both ways | Only if it failed |
| Leasing | prospect (never a tenant) | Leasing | Clara answers and books the tour; person-ask rings the leasing list | Forward to the leasing line; after hours, message | Hours: ring list. After: message | Leasing list |
| Move-in | tenant (future) | Resident services | Clara answers keys, utilities, date from the knowledge base | Message, enriched with their move-in date from AppFolio | Hours: transfer to PM. After: message | Property manager |
| Move-out | tenant | Turnover | Clara logs the notice and explains deposit and inspection | Message | Hours: transfer to PM. After: message | Property manager |
| Rent / balance / payment | tenant | Billing | Clara reads the balance and explains how to pay | Message — never guess a number | Hours: transfer. After: message | Office |
| Collections / late | tenant | Billing | Level 0 for now — always a person | Transfer to the office | Hours: transfer. After: message | Office |
| Renewals | tenant | Renewals | Clara handles the offer, the yes, and month-to-month | Forward to the office line | Same both ways | Only on a decision |
| Lease questions | tenant | Resident services | Clara answers from the client's knowledge base only | Message | Hours: transfer. After: message | Property manager |
| Resident services | tenant | Resident services | Clara answers parking, amenities, packages; complaints always go to a person | Message | Hours: transfer. After: message | Property manager |
| Vendor / contractor | vendor | Maintenance | Shipped behavior overrides this proposal: every vendor call goes to a person, in office hours, even with Maintenance on — Clara never takes a vendor call herself | Transfer to the property manager — a vendor at a door cannot wait | Hours: transfer. After: message | Property manager |
| Verification desk | anyone | Catch-all | Level 0 — never Clara, this is a records release | Transfer to the office | Hours: transfer. After: message | Office |
| Authority / official | anyone | Catch-all | Level 0 — never Clara | Transfer to the property manager; message carries the full statement verbatim | Hours: transfer. After: message + flag | Property manager + org admin |
| Owner / investor | anyone | Catch-all | Level 0 — never Clara | Transfer to the office | Hours: transfer. After: message | Office |
| Ask for a person by name | anyone | Catch-all | Match the name or role to a PropFlow user and ring them | Transfer to the office | Hours: ring them. After: message for that person | The named person |
| Sales / spam / robocall | anyone | always on | Clara ends the call | Clara ends the call | Same both ways | Nobody |
| Everything else | anyone | Catch-all | Transfer to the property manager | Transfer to the property manager; never back to a client-forwarded main line | Hours: transfer. After: message | Property manager |
| No content | — | — | No notice unless we have both a topic and a name; otherwise silence and they call back | Same both ways | Nobody | |
Every topic sits at a level, per client. A topic moves up one level at a time, never two, and only on Fede's word.
The gate to promote a topic is all four of: the module is on for that organisation; a replay set of at least 25 real recorded calls on that topic scores 100% on the right outcome; zero "I'm not sure" or "I can't look that up" endings anywhere in the eval; and a one-switch drop back to level 1 that a session can throw without a deploy.
| Topic | Western Slope today | Camellia today | Honest note |
|---|---|---|---|
| Leasing | 3 — Clara books tours and writes guest cards | 3 | The only topic genuinely at 3. Nine days live at Western Slope. |
| Maintenance request | 3 — Clara writes the work order | 0 — forwarded to their call centre | Camellia never exercises it; Western Slope's 6 calls are not a proof set. |
| Renewals | 0 | 3 | Camellia's 44 renewal calls are our strongest replay set anywhere. |
| Rent / balance | 0 | 1 | Clara has the balance but we have not proven she reads it correctly out loud. |
| Move-out / turnover | 0 | 1 | 7 calls in 5 months. Not enough to promote. |
| Vendor | 1 | 1 | 21% of Camellia's real calls and still level 1. The biggest unclaimed win. |
| Emergency | 0 — built, not hooked up | 0 | Merged dark 9/21–9/22. No client has an on-call list configured. |
| Everything else on the list | 0 — no stored topic exists for them at all | Move-in, collections, lease questions, authority, owner, verification. | |
One classifier, one routing layer, both greeting modes (see greeting is a setting). A menu answer like "leasing" is a hint into the same classifier as an open "how can I help you" — never a second code path.
The rule: the classifier names the topic, then the router reads that topic's level for that organisation. Clara is allowed to attempt an answer only at level 2 or above. At level 0 or 1 she does not try and fail — she says the handoff line plainly ("Let me get you to the right person") and routes. She never says "I'm not sure" or "I can't look that up", because at those levels she was never going to look it up. That sentence existing in a transcript is a bug, not a soft failure, and it blocks promotion for that topic.
A topic Clara cannot classify at all is not a failure either — it falls to "everything else", which has a person behind it during office hours and a full message after hours.
| Question | Options | Recommendation |
|---|---|---|
| Do we adopt one wide topic list now, even though most topics sit at level 0? | (a) Yes — the list is the routing table, levels control behaviour. (b) No — keep three buckets and add topics only as each is built. | (a). The list costs nothing until a level moves. It is how we stop "something else" from being a dumping ground. |
| Vendors are 21% of Camellia's real calls and have no bucket. Do we give them one? | (a) Own topic, level 1, transfer to the PM. (b) Own topic, level 2, Clara confirms the job from the work order. (c) Leave them in "everything else". | (a) first. Biggest miss in the data, smallest change. (b) later once (a) has a replay set. |
| What is the proof bar to promote a topic? | (a) 25 real recorded calls at 100%, zero "I can't look that up", kill switch to level 1. (b) 50 calls. (c) Judgement call each time. | (a). 50 is unreachable for most topics at our volume; a judgement call is how a topic gets promoted on a good feeling. |
| Emergency is built but no client has an on-call list. Who turns it on first? | (a) Ask Western Slope for their on-call people this week. (b) Prove it at Fairhaven first, then ask. (c) Wait. | (b) then (a). Prove on our own numbers, then one conversation with the client. |
Five pieces shipped tonight behind a company switch that is off everywhere, so nothing changed for any client. We also built a proof runner: it lists every number a company's routing could dial, then dials each one.
| Piece | What it does |
|---|---|
| The switch | New routing on or off, per company. Off everywhere today. |
| Emergency first hop | Rings the first person on the on-call list, any hour, before any other rule. |
| Maintenance list = routine person-ask | A person-ask about maintenance rings the maintenance list, like leasing. |
| Catch-all fold | "Something else" rings the catch-all list instead of dead-ending in a message. |
| Topic fold | The first place the caller's topic actually picks the route. |
Number fence: the runner dials only numbers we own and hand it — the bench line +1 970-715-6952 and two of our test phones. Anything else is refused before dialing.
| Call | In hours | After hours | Nobody answers |
|---|---|---|---|
| Emergency | Ring the on-call list, before every other rule | Same — ring, never a message | Down the list, then an "emergency, nobody answered" notice |
| Leasing, caller asks for a person | Ring the leasing list in order, 20s each | Take the message, email it | Clara offers to take a message |
| Something else | Ring the property manager | Take the message, email it | Take a message |
| Timely maintenance | Client's own route (their call center) | Same | A forwarded-call record; never a missed call |
| An empty list | Take a message | Take a message | — |
Fairhaven TEST is ready: one destination, the bench line, and the runner plans a real dial to it. The Willows cannot be proven yet — its person-transfer row still points at two personal cells, so the fence refuses every leg. Point that row at the bench line first; until then there is no Willows proof.
| PR | Concern |
|---|---|
| #10953 | The company switch |
| #10955 | Emergency first hop |
| #10956 | Ring lists; maintenance as routine person-ask |
| #10959 | Topic fold |
| #10960 | Integration branch to main |
| #10954 | Proof runner: enumerate and dial every destination |
What it is: a company-level on-call list for genuine emergencies, one list per asset type (residential, commercial) — same ring mechanics as the leasing on-call list (ring the primary, then the next), but it rings any hour and ignores office hours. A configured list is on; no list is off. Gera's outbound relay call and no-answer ladder for a dropped caller stay exactly as they are.
Every call gets sorted into one of three tiers:
| Tier | What lands here | Where it goes |
|---|---|---|
| Ring now | Life-safety property emergencies (gas, fire, flood, no heat in winter, injury) and authority/urgent-access callers — police with a search warrant, fire department, a process server, a welfare check, a break-in | The on-call list rings immediately, any hour |
| Office line | Time-sensitive but not an emergency — a vendor drop-off, a delivery, a utility call, a tow, an inspector | The office main line, any hour |
| Message | Everything else | Clara takes the message |
When it's ambiguous, Clara asks one question, once: "So I get you the right person — do you need someone right now, tonight?" Authority callers skip the question. If anyone is in danger, Clara says to call 911 first, before anything else.
One rule for every agent (Fede, 2026-09-21): the same ask gets the same outcome no matter which Clara agent takes the call — triage, maintenance, an unknown caller, leasing, or the after-hours desk. One shared tier rule, not one per agent.
Rollout: built dark. Proven overnight on the Fairhaven test line, bench number only — no calls to real people. Turning it on for a client, and who is on their list, is the client's and Fede's call, made after that proof.
| PR | What | Status |
|---|---|---|
| #10663 | Settings keys — emergency.route.residential / emergency.route.commercial carry the on-call people list | Merged |
| #10676 | Resolver — resolveEmergencyHandoff + the 'emergency' verdict role, hands the list to the hunt ring line, hours ignored | Open |
| #10686 | Shared tier rule — same ring-now / office-line / message ask gets the same outcome in every agent | Merged |
| Next | Emergency hunt ticket + urgency field, nobody-answered notice, tool wiring, settings page, decision log | Not yet opened |
Frozen contract and the Smith/Gera revision behind this section: session 001-59 scratchpad, 2026-09-21. No new registry keys — this reuses Gera's existing emergency.route.* keys with the people-list schema.
Live for all customers, bug fixes, no gate:
| Status | What | Evidence | Next step |
|---|---|---|---|
| Proven live, twice | Core path: caller asks Clara for a person, primary answers, call is bridged | Stand-in rehearsal: 2 of 2 accept runs through Clara's front door, ticket acceptedBy set within 17 s, no email; 2 of 2 nobody-answers runs recorded the caller's message and wrote exactly one handoff entry each (02:18:45Z, 02:20:00Z) | Western Slope switched on 2026-09-20 21:35 MT on Fede's yes: Kat Barker first, Beverly Villar second, 20 s each, office hours Mon–Fri 9–5 Mountain, weekends take a message. See the switch-on record. |
| Proven live | Ring chain advances past no-answer | Test 1: rang Fede (3s, dropped), advanced to Gera (8s, voicemail), fell to the message path | Ships as the Phase 1 default |
| Proven live | Press-1 accept | Test 2: Fede accepted, ticket acceptedBy set, bridged | Stays as the fallback mode |
| Now the default (2026-09-22) | Automatic pickup on confirmed human (reported ~3.6s) | Test A3: verdict human landed at +3.6s inside a 12s leg | At the time, AMD returning unknown on Fede's real answer twice in run 3 kept press-1 as the production mode. Shipped in PR #10835: automatic pickup (AMD) is now the default (human_handoff.acceptMode absent = amd); press-1 is the fallback, used only when AMD returns unknown. |
| Proven live | Voicemail detected and skipped | Test A / A2: Gera's leg scored machine_start, chain fell through. Stand-in run 03:03Z: person 1 (voicemail) skipped after 8 s, person 2 rang 2 s later, accepted, bridged call recorded 15 s. | — |
| Off by default (Fede, 9/20 night) | Ring-time text | Twilio SMS SM0d9468531fa7c48c19eb19979c079204, delivered 20:37:46Z / 14:37 MT to Fede's phone, 2s after ring start: “Test Prospect, prospect is calling. Missed it? Call or text this number back and Clara will connect you.” | Turned off by default after Fede's live test: the text arrived but the callback path was not reliable. Stays a per-customer setting, default off (#10336 merged). |
| Proven live | Decline advances to the next person | Run 3, call B: Fede declined, Gera's phone rang 0 s later, nobody picked up, the caller was offered to leave a message | Done. Update 2026-09-22 (#10835): automatic pickup (AMD) is now the default accept mode; press-any-key is the fallback for an AMD unknown verdict, not the default anymore. |
| Proven live | Caller-number passthrough (carrier level) | Test C: Twilio presented the caller's own number, not Clara's line | Stays off as a platform setting — see the caller-ID decision below; removal merged #10223 (part 2 #10225 closed, already covered) |
| Built, not yet proven | Whisper terse (name/role only) | Decided 2026-09-20: whisper content cut to "who, about what"; shipped in #10158, merged into #10157's branch | Willows probe call to prove it live |
| Off by default (Fede, 9/20 night) | Text-on-ring | #10212 merged tonight; per-customer off setting #10250 merged 9/20 | Default off since #10336. Core is: a caller who wants a person reaches the primary, then the secondary. |
| Proven live | Recording of every transferred leg | Stand-in accept run 03:01Z: Twilio recording RE fc948af3… (16 s) on the bridged call, link rows stored against the conversation (#10351) | Per-property setting, default off. Turn on per customer. |
| Proven live | Forward to the client's maintenance line, and its recording | Stand-in rehearsal 03:24Z: a robot tenant on the Willows line asked for maintenance; Clara said "I'll connect you with the maintenance line now" and transferred straight to the stand-in number (Twilio leg 17 s, completed). The full call, maintenance segment included, is on the caller-leg recording RE79038d72… (39 s), kept on the conversation with the transfer time stamped. | Done. Note: a direct forward (no phone-menu step) has no separate recording on the forwarded leg; the audio lives on the caller's leg. Western Slope's maintenance route is a direct forward, so this is the shape it gets. |
| Removed (decided 9/20) | Clara blind-transfer leg for passthrough | #10058 — being closed as superseded now that raw-number passthrough is removed | Superseded by issue #10205; no further work |
| Proven live | Office-hours routing (ring vs. message) | Same Sunday evening: closed hours took a message (03:55Z); hours opened for the window rang the list (03:01Z, 03:06Z). | Done. |
| Proven live at company level | One ring-list-per-role settings | Fairhaven test company (line +1 720 807 1724), list written at the company level only: accept ticket 03:06Z resolved to fairhaven-homes and was accepted; busy stand-in wrote exactly one no-pickup handoff at 03:09Z. | Done. |
| Built, not yet proven | Handoff outcome on the activity ledger | #10211 merged tonight | Willows run 3 tonight |
| Built, not yet proven | Bench voicemail greeting | #10210 merged tonight | Willows run 3 tonight |
| Proven live | Message after hours | 03:55Z (Willows closed): robot prospect asked for a person; Clara said the office was closed, took a note with the caller's number and question, nobody was rung, no ticket. Transcript conv_0401m30y7q2zeawt2dqdkj1hg4x9. | Done. |
| Proven live | Ring list read by the call (verdict fold) | Stand-in rehearsal 9/20 night: every call read the Willows list and rang the stand-in line. | Run 3 tonight at Willows |
| Failed, fix in flight | Callback routing / unknown-verdict handling | Test A2: AMD status-callback 403s in prod; Test C: verdict can land after the <Dial> action already moved on | Fix the signature check and unknown-handling before the next live pass — no PR number in this doc yet |
Western Slope's staff phones run on RingCentral. Their network answers a call within 1 to 2 seconds. Our "press 1 to accept" prompt waited 5 seconds, heard nothing, and dropped the staff member. Callers who asked for a person never reached Kat or Beverly.
Decision (Fede, Sept 21): plain forward is the default. Ring the first person 20 seconds. Whoever or whatever answers is connected, with no prompt and no voicemail detection. No answer or busy moves to the next person. If nobody answers, Clara takes the full message. If the client's phone system sends a call to voicemail, that is their system doing its job. Press 1 and automatic detection remain as per-company options, off by default.
After: live in production at 4:14 PM MT Sept 21 (#10567). Proven at 4:16 to 4:27 PM MT on our own test numbers only.
| Scenario | Outcome |
|---|---|
| First person answers | Connected, no prompt heard, both sides recorded (call CA12e8b62b978077cc7f864765d847a9e7) |
| First person busy | Second person rang 0 seconds later and was connected (CAb56ae0aa5adf9e22e183376f9d7cd319) |
| First person picks up for 1 second and drops | Second person rang and was connected (CA6133e7679e6e7138fa577d226fe5749f) |
| Nobody answers | Clara took the message, one missed-call notice (conversation conv_voice_602a5d2c-a30c-4079-8102-4170f64cfc6c) |
Also shipped the same day: missed-call emails carry the whole message with nothing cut (#10591); the conversation page shows who was rung and what happened (#10575, #10587); every staff leg is recorded from the first second (#10563); playback starts at the real pickup (#10585).
Still open: "something else" callers go to the office main line instead of staff cells (#10586, in review); a phone tree on the forwarded line gets labeled as a voicemail (issue #10626); confirmed on the real Western Slope line Sept 21 at 4:34 PM MT: Kat answered in about 15 seconds, no prompt, both sides recorded.
One destination, one attempt. A machine pickup ends the chain exactly like a human one — no signal reaches the team.
One line, one contact. Clara asks what the call is about, checks whether that module is on for this client, then checks office hours and rings the right list — off forwards to the client's own line, voicemail is skipped, and nobody picking up always falls back to a message.
Everything below is the research, run logs and vendor mechanics behind the summary above. Tables, ledgers and call/session IDs are kept in full; surrounding prose is cut.
A missed hand-off is a dead end today. Clara's hand-off uses ElevenLabs' conference transfer, a black box: no control over the team member's caller ID, no signal when nobody picks up, and the missed-call log shows Clara's number, not the caller's.
Unless the team member opens Clara's follow-up email, the call is simply lost — the most-reported failure in AI-receptionist setups like ours, including two RingCentral community threads describing our exact configuration.
Ask from Fede: "Don't reinvent the wheel — if Twilio and ElevenLabs already solve this, tell me." Target experience: everything recorded including the hand-off leg, the team phone shows the original caller, a failed hand-off returns the caller to Clara for a message, and extension habits still work. All claims below are sourced.
| Behavior we want | Who documents it | What they call it |
|---|---|---|
| Team phone shows the original caller on hand-off | Retell ("Displayed Caller ID: User's Number"), Vapi (callerId = customer number), Synthflow (Twilio-purchased numbers only), My AI Front Desk, EliseAI | Caller ID passthrough, gated behind a setting, works best on Twilio-owned numbers |
| Detect voicemail or no answer, then return to the caller | Synthflow (Human Detection Timeout, six outcomes), Retell, Bland (Hold Timeout), Funnel Leasing (2–3 attempts then the office line), PATLive, EliseAI | Transfer fallback |
| Record the whole call including the hand-off | My AI Front Desk, Dialpad, EliseAI. Caution: Amazon Connect recording stops once an agent leaves an externally transferred call — not a universal baseline | Continuous recording |
| Reach a person by name, extension, or keypad | Dialpad, RingCentral, Nextiva, GoTo (directories); Retell, Vapi, Bland (keypad in parallel with speech) | Directory routing; keypad in parallel with speech |
The mature human-receptionist services (Smith.ai, Ruby, Abby, Nexa, PATLive) don't publish what caller ID staff see on transfer. PM-native AI vendors (AppFolio Realm-X, Knock, Entrata, Colleen, ResMan, Yardi) document almost none of the four; only EliseAI and Funnel are specific. Keypad-during-AI-conversation is a 2024–25 feature most AI-receptionist startups dropped.
| Tool | Vendor | What it gives us | Works with our current native setup? |
|---|---|---|---|
| Fallback URL on the phone number | Twilio | Backup TwiML if ElevenLabs is unreachable — ring a person or take voicemail. Five-minute console setting. None of our lines has one. | Yes; confirm it still honors on an ElevenLabs-imported number. |
Keypad input during the conversation (dtmf_input_settings) | ElevenLabs, added 2026-08-31 | Callers press digits mid-call. Serves the extension habit. | Yes, untested for reliability; bench at the Willows. |
| Blind transfer mode | ElevenLabs | Only mode documented to preserve the caller's number on the team phone. | Yes, one flag; loses the whisper and no-answer signal. |
| Voicemail detection system tool | ElevenLabs | Detects a voicemail greeting. | Presumed yes, accuracy unpublished. |
| Call token on the dialed leg | Twilio | Carries the caller's verified identity onto our own dialed leg with proper trust signing. | Only if we run the dial ourselves. |
| Answering machine detection on the dialed leg | Twilio | 94% accuracy, near 100% in message-end mode. | Only if we run the dial ourselves. |
| Dial action callback with the dial outcome | Twilio | The mechanism for "nobody picked up, bring the caller back." | Only if we run the dial ourselves. |
| Whisper and "press 1 to accept" on the dialed leg | Twilio | Team member hears who's calling before the bridge. | Only if we run the dial ourselves. |
| Dual-channel recording on the dialed leg | Twilio | Guaranteed human-leg recording. | Already true at call start; stays true under our own dial. |
| Caller name lookup | Twilio | Name, not just number, on the phone and in the email. US only, small fee. | Yes. |
| Voice Integrity registration | Twilio | Registers our numbers with carrier spam-analytics programs. | Yes. |
| ConversationRelay | Twilio | Twilio-first architecture, ElevenLabs voices. Native keypad, hand-off, recording. | No — different architecture, large rebuild. |
| Workflows visual builder, transfer nodes | ElevenLabs | Success/failure edges on tool nodes. | Yes, but transfer failure-edge behavior isn't fully documented. |
Confirmed gaps on ElevenLabs: no documented caller ID on conference transfers, no ring timeout, no resume-after-failed-transfer, no transfer outcome in the post-call webhook, and an open bug where the transfer cuts Clara off mid-sentence.
| Path | What it gets us | What it cannot do | Size |
|---|---|---|---|
| A. Stay native, configure harder | Fallback URL, keypad, blind-transfer caller ID, voicemail detection, name lookup | No reliable "bring the caller back"; no whisper on blind; conference caller ID stays undocumented | Small — settings plus a Willows bench |
| B. Native conversation, our own hand-off through Twilio | Every target behavior: real caller ID with trust signing, whisper, voicemail detection, return-to-Clara, structured message, continuous recording, keypad/name routing | Needs a custom transfer tool and a bridge handler | Medium — the shape Retell, Vapi and Bland already implement |
| C. Twilio-first (ConversationRelay or SIP trunk) | Cleanest control | Rebuild of the call layer; ElevenLabs SIP mode has no whisper | Large, not now |
This week (Western Slope goes live on forwarding):
Next two to four weeks, one property at a time:
Questions for support before relying on anything: ElevenLabs — conference-transfer caller ID, ring timeout, resume-on-failure, recording after the agent leaves. Twilio — does the fallback URL fire on an ElevenLabs-imported number, and can we regain the caller after our own dial reports no-answer.
~/westernslope-data/phone-tree.md, probed 2026-09-05.| Rank | Pitfall | Source |
|---|---|---|
| 1 | Caller ID replaced by the AI's number on the transfer leg | RingCentral forums, Retell community, Canadian carrier reports |
| 2 | ElevenLabs conference transfer cutting off early | ElevenLabs / Twilio field fix |
| 3 | Voicemail mistaken for a person, so the AI "succeeds" into a voicemail box | Field reports |
| 4 | Codec and touch-tone distortion on multi-hop bridges | Field reports |
| 5 | Echo and half-duplex audio right after a transfer | Retell community |
| 6 | Unowned caller ID blocked or spam-flagged | Twilio spam remediation docs |
| 7 | Per-leg cost stacking — Twilio bills each leg, plus a small fee to receive a transferred call | Twilio billing docs |
| 8 | Single-vendor outage with no fallback | ElevenLabs status history |
| 9 | Ring-timeout and busy-line handling gaps | Field reports |
| 10 | Recording consent on the transferred leg in all-party-consent states | Recording-law surveys |
| Option | What it is | Fixes | Leaves broken / cost |
|---|---|---|---|
| A | Keep everything as is | Nothing — cheapest, zero rework | Dead-end missed calls, late no-answer detection, the cut-off bug |
| B | Switch ElevenLabs to blind transfer | Caller ID on the team phone shows the real caller | Loses the whisper; still no no-answer signal or failover. Not enterprise-grade. |
| C recommended | Own the hand-off on Twilio: Clara's transfer becomes a tool call to our backend, which dials the team member with the caller's real number (or Clara's, per property), whispers who's calling, detects voicemail, rings up to N seconds, and brings the caller back to Clara on no-answer | Real no-answer signal; two-channel human-leg recording comes free; full control of caller ID, ring timeout, voicemail detection | Rewiring every specialist agent's transfer tool, a new bridge handler, a Willows bench, an overnight soak, then a one-property-at-a-time rollout. Spam-label risk on the real caller's number — default is Clara's number plus whisper, "show the caller" as a per-property switch. |
| D | Twilio-first with ElevenLabs behind a SIP trunk | Maximum control and recording regardless of transfer mode | Bigger rebuild; SIP mode has no whisper; had the July incident. Not now. |
Option C was called "own the dial" in the June design note, deferred then as too much rewiring.
Yes, once the main number forwards to Clara. Callers say the name; direct lines still work for anyone who already has them.
A PropFlow number, not the caller's. Option C can show the real caller instead, with the spam-label trade-off above.
Yes — the whole call, hand-off included, today and under option C. Only SIP REFER would lose it, and we don't use it.
| What | Before | After |
|---|---|---|
| "Talk to a person" | A message plus the missed-call email. | Clara rings each person on an ordered list (~20s each), then falls back to the message and email. Only Willows has a list today. |
| Vendors calling in | Clara worked the call herself. | Every vendor goes to a person: staff in office hours, the closed-office line after hours. Live for all customers. |
| Everything else Clara can't serve | No forwarding option. | Forwarded to the client's own office line, set per company. Dark for Western Slope until they set a number. |
Proof
CA16cded30f4, 5s) but nobody's phone rang: the ticket was keyed on the caller's number while the leg arriving at the ring line carries the property line's number, so the lookup missed.Principle (Fede, 2026-09-19): use what Twilio and ElevenLabs already solved — recording, ring-one-at-a-time dialing, voicemail detection, staffing queues — instead of hand-building any of it.
| Vendor feature | What it actually does (documented) | Source |
|---|---|---|
Twilio call status completed | Means audio flowed — a person, a menu, or a voicemail box. Never proof of a human. We decide "answered" from the keypress, not the status. | Call resource |
<Dial record="record-from-answer-dual"> | Dual-channel recording of whatever leg it's on, from answer. Attach to every Dial leg and each one records itself. | Dial |
<Dial sequential="true"> with several <Number> nouns | Calls "up to ten agents one after the other," in order — a native ring-chain, no custom hop webhook needed. | Dial |
Dial timeout | Defaults to 30s per leg; Twilio adds ~5s buffer, so a "20s ring" runs closer to 25. | Dial |
Dial callerId | On an inbound-triggered dial, shows the original caller by default. To show anything else, the value must be a number we own or have verified — never spoofed, never the caller's number on an inbound SIP leg. | Dial |
Answering Machine Detection, DetectMessageEnd | "Close to 100% accurate" in the US, $0.0075/answered call. AsyncAmd=true lets the callee connect immediately while detection runs in the background. | AMD FAQ, AMD |
| TaskRouter "call" instruction | Dials a worker's cell from a Twilio number, runs TwiML on the worker's leg — the hook for a "press 1 to accept" Gather. Default timeout, optional dual-channel record. | TaskRouter |
| TaskRouter time-of-day routing | Evaluates in UTC, not property-local — a build detail, not a design choice. | Time-of-day routing |
| TaskRouter on-call rotation | Not built in. Time-of-day routing is a static weekly schedule; "who's on call" has to be our own setting, flipping a worker's Activity. | Time-of-day routing |
| TaskRouter pricing | ~$0.06/task after the first 100/month free. Not on Twilio's own pricing page — treat as an estimate. | Twilio pricing search, unconfirmed on twilio.com/pricing |
ElevenLabs transfer_to_number | A conference transfer: destination dialed and added to a conference with the caller, agent removed. Not a blind forward, not SIP REFER. | Transfer to number |
| How an ElevenLabs agent ends a call | Not only end_call: a max_duration_seconds ceiling (60–7,200s) also ends it, plus a separate channel-specific inactivity timeout. | Conversation flow |
Listen: 4-minute episode
Harness: a real-call ring-chain harness (#10030, #10033 and two siblings) is the acceptance test for everything below; nothing turns on at Western Slope until two clean sweeps.
Two hard requirements (Fede, 2026-09-19): the whole call is recorded end to end, including after pickup and after any transfer. Staff use their own cell phone — nothing to install. Ring lists are plain cell numbers.
Every dial or transfer leg carries <Dial record="record-from-answer-dual"> — the caller-facing leg, the ring-line leg, and the leg to whichever cell finally picks up. Each leg produces its own recording, listed separately on the conversation record — Twilio does the recording, not code we wrote.
| Leg | Recorded today | Real calls checked | Fix |
|---|---|---|---|
| Caller ↔ call center (after Clara hands off) | 0 of 35 — never, not intermittent | 35 | Add record="record-from-answer-dual" plus a recording status callback to both bridge builders (bridgeTwiml, maintenanceBridgeTwiml); recording attaches to the conversation. In flight, PR to follow. |
| Caller ↔ Clara | Recorded at start on every call; audio deleted afterward by policy unless a human transfer is detected → 3 of 31 kept | 31 | A retention rule (agents/clara/lib/voice/post-transfer.ts), not a recording bug. Misclassification causing most deletions fixed 2026-09-18 (#9565, #9716, related #9621). Policy itself still contradicts "record every leg, always" — a "keep every call recording" company setting, default off, is in flight. |
Root cause on the transfer leg: the hand-off opens a new outbound call from the property line to a PropFlow bridge number, with no ParentCallSid link back to the inbound call, and the bridge TwiML never had a record attribute. ElevenLabs never sees this leg either. Example: 2026-09-18 23:58 UTC, 351s with Clara then 323s with the call center — both real human legs, but the Clara-side recording was misclassified and deleted, and the transfer leg was never recorded; 2026-09-18 19:36 UTC, same double gap, caller hung up after 37s. Both examples predate #9565/#9716. The Sentry "not eligible for recording" issue is harness noise from a fake CallSid, not a real call. Both fixes ship dark, default off, Fede turns on per customer.
Real calls from PropFlow tester numbers into the Willows ring line (+1 970-615-5171) with a seeded handoff ticket, staff list Fede then Gera — Twilio's own call records are the evidence.
| Test | What happened | Evidence |
|---|---|---|
| Ring chain, nobody presses 1 | Rang Fede (3s, dropped), advanced to Gera (8s, voicemail), fell through to the message path; total 39s | Twilio call CA8c25dd2ef82efccd4b289a66dc006bd8, child legs CAec39c37672d49133904f2a7fdec788df (Fede) and CA8f9803a8a89a28f049b314b469842dbb (Gera); ticket acceptedBy empty |
| Ring chain, Fede presses 1 | Accepted; caller bridged | Ticket HUNTTICKET#+17206795566 / 2026-09-20T15:56:22.110Z acceptedBy +1 404-285-9387 at 15:56:34Z; Fede's leg 16s |
| Recording | Caller's leg recorded on every run | Recording on CA8c25dd…, 22s |
Voicemail detection, voicemail-style greeting on bench number +1 970-715-6952 | Machine, both modes | MachineDetection=Enable → machine_start (15s); DetectMessageEnd → machine_end_other (28s) |
| Voicemail detection, Fede answering "hello" | Human, twice | answered_by=human on two calls (26s, 12s) |
| Staff phone with "Silence Unknown Callers" on | Answers and drops in 2–3s, no whisper, no accept; chain advances correctly | Four legs of 2–3s before Fede saved the ring line as a contact |
Enable, no keypress — becomes the pickup method; press 1 stays the fallback.Open trade-off — "Silence Unknown Callers" drops the ring unless the number is a saved contact. Showing Clara's line works once staff save it once; showing the real caller (the platform passthrough setting) gets silenced. Fede to decide: (A) real caller's number, onboarding tells staff to allow unknowns; (B) Clara's line, staff save one contact.
Second round over a tunnel into the Willows hunt chain, testing automatic pickup end to end: a plain human pickup, caller-number passthrough, a bench "voicemail," and a decline. Fede answered B and C normally and declined E; test D (the product path through fastlane) wasn't run — Willows was still in message mode. All times MT.
| Test | Chain | AnsweredBy | Whisper served (verbatim) | Accepted by | Verdict | Call SIDs |
|---|---|---|---|---|---|---|
| A 12:13 | Fede → Gera, flag still on press-1 | both legs null | "Sam Prospect, prospect. Press 1." (inside a <Gather>) | none | Chain-advance PASS; confirmed the writer-script bug below | Fede CA7f94866f…581d 9s, Gera CA2366b9…b387 4s |
| A2 12:24 | Fede → Gera, AMD on | Fede unknown 18s; Gera machine_start 78s | "Fede test caller, prospect." | none | FAIL — AMD callback 403'd, Fede's pickup never counted; chain fell to Gera's voicemail, then message-taking (RE5e745f…) | Fede CAac976c…f8c, Gera CAa38ab0…6ea1 |
| B 12:40 | Bench "voicemail" → Fede | Bench unknown 8s | same plain whisper | bench leg (wrong) | FAIL, design flaw — the bench rig speaks immediately, scored unknown; with unknown = accept, the chain stopped at the fake voicemail and Fede's phone never rang | Bench CA631a8f…12b0 |
| A3 12:41 | Fede → Gera, AMD on, signature bug patched | Fede human 12s, verdict at +3.6s | same plain whisper | Fede, automatically | PASS — automatic human pickup, no keypress, verdict at 12:41:11, chain stopped | Fede CA7c9557…fef2 |
| C 12:43 | Passthrough ticket, Fede only | Fede unknown 3s, from +17205864069 (tester's own number) | same plain whisper | Fede | Passthrough PASS at carrier level; but the 3s leg ended before the ~10s-late AMD verdict, so the <Dial> action already called it "not accepted" and the caller fell to message-taking | Fede CA35322f…2c95 |
| E 12:44 | Fede declines | Fede unknown 9s, status completed; no Gera leg | same plain whisper | Fede (wrong) | FAIL — decline hit carrier voicemail, scored unknown within 6s, and unknown counted as accept: chain stopped, caller bridged into Fede's own voicemail instead of advancing | Fede CA0363da…8fc2 |
| D | Product path via fastlane | Not run — Fede was on the phone for E; Willows still in message mode, needs the handoff-mode change first | — | |||
Headline: automatic human pickup PASS (test A3, 3.6s). Passthrough PASS at the carrier level (test C). Decline FAIL — reaching carrier voicemail scores unknown, and unknown is today treated as accept, so the chain stops instead of advancing (test E). Two mechanical defects: the AMD status-callback 403s in prod (test A2), and the accept verdict can land after <Dial>'s action already moved on (test C's late-verdict race). Separately, the writer script arming AMD mode wrote to a local settings file instead of prod DynamoDB on its first run (test A) — the prod-table assert passed while the actual write missed the table.
Design change needed, at the time: only a confirmed human verdict should auto-accept; unknown should fall back to whisper-plus-press-any-key, not silent accept; machine should advance the chain, not stop it. Update, 2026-09-22 — shipped in PR #10835: automatic pickup detection on staff ring-list calls is now LIVE. Default is Twilio AMD (human_handoff.acceptMode absent = amd) — no keypress needed on a confirmed human. Press-any-key is the fallback, used only when AMD returns unknown. Still open: machine_start currently hangs up the call; Fede is deciding whether to treat it as unsure instead. Per-client routing (which mode a given customer runs) is tracked on the Clara onboarding playbook, the source of truth for that.
Write ledger (all restored, verified): ring line webhook pointed at the tunnel 10:44, restored to prod 12:44. Bench line webhook pointed at the tunnel 12:29, restored 12:44. huntAcceptMode flipped press1→amd in prod Dynamo 12:23, restored to press1 12:45; callerIdPassthrough untouched (false) — the signature and unknown-handling defects are still open. No Camellia or customer data touched; dev server and tunnel stopped, worktree clean.
Fede tested from his own phone first. What happened, in order:
Fede's ruling: prove it on lines we own before calling him again. The rest of the night ran on the stand-in line (+1 970 715 6952, a test number that answers and presses 1, plays a voicemail greeting, or reports busy on request). Blocker found and fixed on the way: the site's login wall was refusing Twilio's calls to that test line (#10346).
| Scenario (through Clara's front door, Willows) | Runs | Result |
|---|---|---|
| Prospect asks for a person, primary answers | 2 | Pass both. Person accepted within 17 s of the ring, ticket marked accepted, no missed-call email. |
| Primary busy, nobody answers | 2 | Pass both. Caller recorded a message, transcript captured ("please call me back"), exactly one handoff entry per call about 90 s after the call. |
| Person 1 voicemail, person 2 answers | 1 | Pass. Person 1 skipped after 8 s, person 2 rang 2 s later, accepted, call recorded. |
| After hours (office closed) | 1 | Pass. Clara said the office was closed, took a note with number and question, rang nobody. |
| Accepted call is recorded | 1 | Pass. 16 s recording on the bridged call, stored against the conversation. |
| Company-level list (Fairhaven test company, its own line) | 2 | Pass. Accept resolved to the right property and was accepted; busy stand-in produced exactly one handoff email. |
Every test setting (stand-in lists at Willows and the test company, Sunday hours, stand-in line behaviours) was restored afterward; Camellia's routing checked identical before and after. Harness grader gaps filed as #10362.
Setup: the Willows company's maintenance route pointed at the stand-in line for the test (written 03:24:06Z, ended 03:35:28Z, back to unset). Recording of transferred legs was already on for Willows.
Fede's words: "once you're highly confident this works, then you can go ahead and do Kat first, Bev second." Written at the company level, so every Western Slope property gets it:
Decided (Fede, 2026-09-20)
Fede's framing: vendor behavior is a reference, not a template — Clara is AI-first, not a phone tree.
Next build: text-on-ring (who, why, caller's number, tap-to-call), dark at Willows, plus a contact-card onboarding step. Filed for later: staff call Clara back for a summary of a handed-off call.
| System | Caller ID on the staff cell | Screening |
|---|---|---|
| RingCentral | Original or business number, caller's choice | Press 1 |
| Dialpad | Original or Dialpad number | Say your name, press 1 |
| OpenPhone / Quo | Original caller | States name before ringing |
| Google Voice | Original or GV number toggle | States name, press * |
| Grasshopper | Caller's choice | Press 1 / 2 |
| Ooma Office | Caller's choice | Press 1 — so the cell's own voicemail can't swallow the call |
| Zoom Phone | Original | Press 1, unless a company contact |
AI receptionists and leasing assistants converge on a warm transfer with a briefing, no keypress:
| Vendor | Caller ID | Handoff |
|---|---|---|
| EliseAI | Original caller | Warm, context passed, configurable fallback |
| Smith.ai | Caller's number when caller ID is on | Warm, receptionist briefs |
| Ruby | Ruby's number, keeps staff phones private | Warm |
| Property Meld | Business phone only, cells hidden | Warm |
| Goodcall | Local number | Warm with summary |
| Synthflow | Agent's number by default; original caller only on enterprise Twilio numbers | Whisper first |
| Bland | Configurable | Proxy agent briefs, then merges |
Recommendation (AI-first):
Consequence: the platform passthrough setting stays built and OFF, not the default. Option A (real number as default) requires staff to disable unknown-caller silencing and breaks under iOS 26 screening.
Proposed — pending Fede
Problem: three separate settings answer one question today — message-vs-transfer mode, the ring list, and the company office-line catch-all. Clients differ only in headcount: Camellia one (Joanna), Western Slope two, others three or four.
Proposal: one ring list per role (leasing, maintenance) at the company level, property-level only when it truly differs. Business hours decide, not a mode: during hours a caller who wants a person is always rung; nights/weekends Clara takes a message by default. Decided (Fede, 2026-09-20): a maintenance emergency after hours always rings, weekends included. Leasing is never an emergency — after hours it takes a message. Client inputs are the two lists plus office hours, a setting of its own, separate from tour availability.
| List contents | What happens |
|---|---|
| One entry = office number | Forward to the main line |
| One person | Camellia today |
| Two people in order | Western Slope today |
| Three or four | Same list, longer |
| Rotation (later) | The order changes by schedule, same object |
| List exhausted | Clara takes a message, always, not a setting |
Removes: the message-vs-transfer switch, the separate catch-all setting, per-property duplicates. Keeps: the ring chain as built, recording, automatic pickup.
Fede's module layer (2026-09-20): leasing, maintenance, and renewals sell separately. A client can have leasing on and maintenance off. The ring list above only runs once a call has already been routed to the right function — the missing layer decides, per client, whether that function is Clara's to handle at all, or should forward straight to the client's own existing line for it.
| Piece | Where it lives | Used by phone calls? |
|---|---|---|
| Module switch per client (leasing/maintenance/renewals on or off) | Admin company page | No — not read by voice |
| Maintenance handle-or-forward setting per property | Maintenance intake route setting | Yes — Western Slope's call center |
| Company office line for everything else | Catch-all setting | Yes — off everywhere today |
| Human handoff transfer/message per property | Handoff route setting | Yes — Camellia |
| Coworker/autonomous switch per property | Property handoff mode | Yes, but not about modules |
| Ring lists per role | New, PR #10209, dark | Not yet live |
Today these five pieces are separate and only two of them (maintenance, human handoff) actually decide anything on a live call. The module switch exists at the company level but nothing in the voice path reads it yet.
The engine: one decision per call, run in order — topic, then is that module on for this client, then is the office open, then the result.
Smith's registry review 2026-09-20: resolver reuses the existing forward-to shape (maintenance.intake_route generalized per function), emergency resolves via emergency.route.*, renewals stays a module; two questions with Gera (renewals row vs topic; ring_list as successor of transfer.<role>).
| Topic | Module on? | Office open? | Result |
|---|---|---|---|
| Leasing | On | Open | Ring the leasing list |
| Leasing | On | Closed | Clara takes a message |
| Leasing | Off | — | Forward to the client's own leasing line |
| Maintenance | Emergency | — | Ring the maintenance on-call list, any hour — even if the client has a call center |
| Maintenance | Not an emergency, client has a call center | — | Forward to their call center, never ring their people |
| Maintenance | Not an emergency, Clara handles it | Open | Ring the maintenance list |
| Maintenance | Not an emergency, Clara handles it | Closed | Clara takes a message |
| Renewals | On | — | Clara handles it |
| Renewals | Off | — | Forward to the office line |
| Everything else | — | Open | Office line, or the leasing list if there's no office line |
| Everything else | — | Closed | Clara takes a message |
| Any list exhausted | — | — | Clara takes a message |
Two people can be pinged about one missed call (the rung person by text, the owner by email); the text stays off until Fede turns it on.
| Moment | Who gets it | What it says | Status |
|---|---|---|---|
| Phone rings | The person being rung | Short whisper: caller's name and role; Clara's number on the screen | In the pickup PR (#10157), armed |
| Phone rings (optional) | The person being rung | A text naming the caller and topic, no phone number; reply goes through Clara | Live, on by default, per-customer off switch (#10250) |
| Nobody picks up | Escalation owner, else the property inbox | Missed-call email: who, when, what they asked, quote, call-back line | Exists today, on at Willows only; follow-up #10246 makes the call-back line go through Clara instead of the raw number |
| Any outcome | The activity ledger on the record | Handoff accepted / no pickup entry | Merged (#10211) |
What changes in the build chain: one routing resolver that reads the module switch, a per-module forward-to number (new company-level setting, one per module), office hours, and the ring lists. The maintenance handle-or-forward setting and the office-line catch-all fold into it — they stop being separate settings and become the module-off destination for their own module. Camellia's typed number is one row in that resolver, not a special case.
| # | Step | PR | Status |
|---|---|---|---|
| 1 | The ring-list building blocks and the lookup that reads them | #10209 | Merged |
| 2 | Company office hours become their own setting | #10208 | Merged |
| 3 | Fold the hand-off decision (office hours + ring list) into one verdict | #10213 | Merged 9/20 |
| 4 | Add the per-module forward-to number and fold the module switch into the same routing decision | — | Building |
| 5 | The call route reads the new resolver instead of the old separate settings | — | Not started |
| 6 | Clara's phone tree learns leasing and renewals forwarding, not just maintenance | — | Not started |
| 7 | The ring ticket carries which role it's for (leasing, maintenance, renewals) | — | Not started |
| 8 | Maintenance emergencies after hours ring the on-call list | — | Not started |
| 9 | Writer and backfill scripts to set the new settings from what's live today | — | Not started |
| 10 | A settings screen, "Phone routing," for clients to see and edit this | — | Not started |
| 11 | Retire the old separate settings once the new ones are proven | — | Not started |
Module off = never Clara; a client with a few buildings on Clara models as module on + company forward-to + those buildings set to Clara.
If module status can't be read during a call, Clara does today's behavior, never forwards to nothing.
Kept for history. Phase 1 shipped (our own dial on the ring chain). Phase 2 (on-call rotation) is now an onboarding answer on the ring list, not a build. Phase 3 (Twilio TaskRouter) is dropped: call-center queues are legacy VoIP, and the target is one dial path on Gera's lookup, described under Where the phone line is going.
<Dial sequential="true"> across the ordered ring list, each <Number> carrying AMD, record="record-from-answer-dual", and a 20s timeout. The "press 1" whisper is now the fallback, not the default — Twilio's own docs confirm sequential is a native ring-chain, not something we're building.Not a phase of this rollout — picked up later. Facts gathered now so the option is scoped when that lane resumes.
What's possible: Twilio Branded Calling shows the business name (Basic 15–32 chars; Enhanced adds a logo and call reason) on T-Mobile and Verizon only, not AT&T. Needs a Trust Hub business profile per brand, one bundle per display name. Multiple customer brand names under one PropFlow account needs secondary customer profiles (ISV model) and our primary profile approved as an ISV Reseller/Partner first. ~7 days brand review plus 1–2 days carrier propagation; per-call pricing unpublished.
Cheaper, narrower options: CNAM (legacy caller-name lookup, 15 chars, mostly landlines) is $0.01/lookup, no setup. SHAKEN/STIR A-attestation earns a "verified" mark and lowers spam risk on its own. Voice Integrity (~$10/month) registers our numbers with carrier spam-analytics programs. None of these show a name — just fewer spam flags.
SMS has no equivalent — 10DLC carries no brand name; RCS is the closest analog, a separate project.
Proposed, when this lane resumes: Step 1 — Voice Integrity, SHAKEN/STIR, CNAM per customer number now, cheap, no extra paperwork. Step 2 — Branded Calling per customer once we have their EIN, address, and a signed one-page authorization. Showing the customer's own name is the recommendation over "PropFlow."
All of this is registration with Twilio, never a code change alone — every submission is Fede's call. Sources: Branded Calling, Basic · Branded Calling, Enhanced · CNAM · Voice Integrity · Secondary compliance profiles · Alphanumeric senders (SMS)
| RingCentral / call center | Clara + Twilio | Status |
|---|---|---|
| Auto-attendant menu ("press 1 for leasing") | Clara asks and understands; no menu | Works today |
| Ring groups / hunt (sequential or simultaneous) | <Dial sequential="true"> ring list per role, whisper accept | Planned (Phase 1) — hop-by-hop version works today |
| Business hours / after-hours rules | Vendors and after-hours already skip to the office line | Works today |
| On-call rotation | Per-role settings, static two-person now, weekly rotation later | Planned (Phase 2) |
| Voicemail to email | Missed-call message recorded and emailed to the role's team | Works today |
| Continuous call recording through a transfer | record-from-answer-dual on every leg | Dark — audit running, see above |
| Staff caller ID on callback (sees the real caller) | See the decision below | Open question |
| Call center queues, escalation, per-agent timeouts | TaskRouter workers/queues per role | Planned (Phase 3) |
| Branded caller ID on outbound | Twilio Branded Calling / Voice Integrity / CNAM | Later — with the renewals lane, not this rollout |
| Desk phones, extensions, landline behavior | Not needed — staff use their own cell | By design |
Reply with a letter per decision, or say nothing and the recommendations ship, dark, on the ladder above.
Ring line: +1 970-615-5171 (our hunt webhook, Western Slope today). Whisper "press 1 to accept": #10030. Ring-chain harness (acceptance test for everything in this section): #10030, #10033, plus two sibling PRs. ElevenLabs transfer tool: transfer_to_number, conference mode, agent leaves on join. Recording: attach <Dial record="record-from-answer-dual"> to every dial/transfer leg, one recording row per leg — never one stitched file. TaskRouter time-of-day expressions are UTC, not property-local. Nothing in Phase 1–3 turns on at a real property until two clean sweeps of the harness pass — see the standard rollout ladder at the top of this repo's CLAUDE.md.
Internal: docs/planning/unknown-caller-transfer-no-answer-detection.md (June 2026 design, options 2a–2d); agents/clara/lib/voice/post-transfer.ts (recording at answer, outcome classification); config/phone-registry.json; agents/clara/lib/voice-agents/prototypes/western-slope-portfolio-triage.ts (bridge callerId).
How we host a property's phone number and hand a caller off to a person — what's proven live, what's decided, and the research behind it.
Proposed 2026-09-07 · deep dive 2026-09-08 · live-tested and restructured 2026-09-20 · current state + long-term vision added 2026-09-22 · topic model proposed 2026-09-22 · inspection and two-mode design 2026-10-07 · call routing end-to-end inspection and holistic design 2026-10-07 evening