Video is Clara's fourth way in, next to phone, text and email. Same Clara, same tools, same data.
Proposed — pending review Architecture added 2026-10-01 night; voice-agent framing added 2026-10-05. Not turned on for any customer.
Staff talk to Clara on video, either from our website or by inviting her into their own Zoom, Meet or Teams meeting. In a meeting she mostly listens. After the meeting, what the team said about each tenant goes to the renewals engine as notes for staff to approve. The near goal: Clara sits in on a client's renewals meeting next week.
Recommended: video is a fourth channel into Clara's one engine. It isn't free: that engine waits for the whole answer before speaking and loads a huge prompt for staff. Five changes fix that; dev measurements (Sonnet 4.6, two runs each) put her first word at 0.7–2.9 s.
Details
Proposed — pending review. Fede, 2026-10-07, after the 3:55 PM client meeting: "Clara just couldn't talk at all… I don't think the current design is going to work for a meeting with a lot of interruptions."
What happened. One-on-one Zooms worked. The group meeting did not: Tavus heard the whole room as one speaker and never found a gap.
| Zoom test today | Length | Speaker turns | Clara turns |
|---|---|---|---|
| 2:08 PM, one person | 6 min | 20 | 19 |
| 2:16 PM, one person | 9 min | 24 | 20 |
| 3:55 PM, client group, people talking over each other | 8 min | 1 | 1, cut off after "Hey, I'm" |
Why. Three things stack up. The live persona is set to high interruptibility, so any sound stops her. The wake phrase the design called for was never set on the persona, so she tried to answer everything. And Tavus gives us no per-speaker audio in a meeting, so our engine cannot tell who is talking or whether it was addressed; it only sees one merged "user" turn. (Sources: persona p3f51613c91e read 2026-10-07; Tavus conversational-flow and meetings docs.)
What Tavus offers. Interruptibility low / medium / high ("low: keeps talking through minor interruptions"), turn-taking patience, the sparrow-2 turn model ("fewer interruptions in noisy settings"), and wake / sleep phrases. There is no "never interrupt" or "finish the sentence" mode, and nothing documented about several speakers.
| Option | What changes | Cost | Keeps the face? | Bet |
|---|---|---|---|---|
| A. Tune Tavus and retest (recommended first, today) | Interruptibility low, patience high, wake phrase "Hey Clara" / sleep "Thanks Clara", prompt: finish the answer, keep it to two sentences. Retest in a 3-person Zoom with deliberate crosstalk. | 1 hour | Yes | Might be enough for "mostly listens, answers when asked". Will not fix "who is talking". |
| B. Different carrier for meetings (if A fails) | A meeting bot that hands us each speaker's audio (Recall.ai, or Zoom's own real-time media stream). Our engine decides when to talk, holds the floor until the answer is done, speaks through the bot. The website one-on-one call stays on Tavus. | 1–2 weeks | No face in meetings (voice only), unless Recall's GPU output video proves out | Solves crosstalk and "was I addressed" for real. Recall is about $0.50 an hour plus $1 an hour for audio out. |
| C. Answer in text in group meetings (cheap floor, pairs with A or B) | Clara listens, posts answers to a live notes page or the Zoom chat, and speaks only when addressed by name. Nobody gets talked over. | 2–3 days | Yes | Matches the original "listener" design and the renewals-notes goal. |
Applied 2026-10-07 18:35 MT on Fede's go ("go with low", "go with all the best for group settings"): live persona p3f51613c91e now has interruptibility low (was high) and turn-taking patience high (was medium). Wake phrase not set yet: the persona is shared with the website one-on-one call, so a wake phrase there would silence Clara until "Hey Clara" on every call. Next retest: a 3-person Zoom with crosstalk.
Recommendation. Do A today as the go/no-go, with the retest recorded here. If she still cannot finish a sentence in a 3-person crosstalk test, go to B for meetings and keep Tavus for the website call. Build C either way: it is the shape that works in a room full of people.
Other bugs from today's practice calls (2:08 and 2:16 PM one-on-one Zooms, from the transcripts):
Possible, but tight. A staff-only meeting where Clara listens and answers basic questions needs 6 of the 9 engine PRs, most of a seventh, and the meeting join itself, all proven at Fairhaven first.
| Step | Needed for the demo? |
|---|---|
| 1. Staff questions stop loading the leasing prompt | Yes, for speed |
| 2. Staff write-list consistency cleanup | Can wait (no exposure) |
| 3. Video as a channel | Yes |
| 4. Staff person from the call binding | Yes |
| 5. Portfolio reads as one capability | Yes |
| 6. Streaming answers from the engine | Yes |
| 7. The answer line Tavus calls, with its read-only gate | Yes |
| 8. Session-start snapshot and warm-up | Mostly: the snapshot yes, the AppFolio part can wait |
| 9. End-of-call cleanup | Can wait |
| Meeting join: link, wake phrase, 15-minute rejoin | Yes. It's on the critical path, outside the 9, and untested on a real Zoom |
Fallback if it slips: Clara joins and only listens. She answers "I'll follow up after the meeting," and the transcript still feeds renewal notes. That needs just the binding, the meeting join and the transcript callback. Proposal.
v1 meetings are the client's own staff only. Tavus docs don't say what attendee or invite details Clara sees, so she can't detect outsiders yet.
Program plan: the Western Slope pilot (notes → brain → renewals) lives on the company brain page.
Proposed — pending review. Fede asked 2026-10-03: "can i test on zoom?" → "scope it".
How she joins: Tavus can send Clara into a Zoom, Google Meet or Teams link. We pass the link as meeting_url when we create the call, and her face and voice join as a normal attendee. No Zoom app review is needed for that path. (Tavus API, Tavus FAQ)
Already built for the website call and reused: creating the Tavus call, the company binding, Clara's answer line, the company snapshot, the end-of-call callback, Sonnet 5.5, and the reply timer.
| Step | What | Size |
|---|---|---|
| 0. Test call (gate) | One 15-minute call into our own Zoom with the made-up "Pinecrest" company. Needs a Tavus conferencing address set up on the PAL. Answers the five unknowns below. | 1 hour, Fede's go |
| 1. Invite box | "Invite Clara to a meeting" field on the video page: paste a Zoom link. Creates the meeting session, owned by the signed-in person's company. | 1 small PR |
| 2. Join and listen | Create the Tavus call with meeting_url. Clara speaks only when addressed ("Hey Clara"). | 1 small PR |
| 3. Rejoin | Only if the test shows the 15-minute cap applies in meetings. | 1 small PR |
| 4. Renewal notes | After the meeting, the transcript becomes renewal notes per tenant. | 1 small PR |
Five unknowns the Tavus docs don't answer (all say "not stated"):
If unknown 1 fails: a meeting-bot service carries the audio and our own engine answers. The candidates are Recall.ai (about $0.50 an hour; pricing) and the open-source Attendee (GitHub). That path is bigger, and Clara probably has no face in the meeting.
Video is a fourth channel into Clara's one engine, the same loop that answers texts and emails. Each spoken turn enters the engine directly, the way staff clarifying questions already do. The speaker is the signed-in staff person from the call binding, not a phone number. Ask Clara's 23 portfolio reads become one capability of that engine, STAFF_PORTFOLIO. In v1 a video turn can only read: a new gate allows read tools only. AppFolio data goes into a snapshot built when the call starts, under the per-account request budget, never as a live AppFolio call on each question.
The honest cost. Fede's direction is right, but it isn't "nothing to rebuild." Today the engine doesn't stream, a staff question loads about 57k tokens of leasing prompt, and there's a long setup before the model is called. Five changes make it fast enough to talk to:
A sixth change, skipping the resident and prospect setup steps, is needed only if a bench trace shows that setup is slow.
| Dev measurements, first spoken word | Answer already in snapshot | After one lookup |
|---|---|---|
| Sonnet 4.6 (voice tier) | 0.7–0.8 s | 2.2–2.9 s |
| Haiku 4.5 | 0.4–0.9 s, but still reached for a lookup | 1.3–1.7 s, but picked tools worse |
| Sonnet 5 (email and Ask Clara tier) | 1.2–1.8 s | 4.6–5.1 s, too slow |
Dev measurements: Fairhaven test company, N=2 per cell, warm cache, with the changes above in place. Lookups on a one-property dev company took under 60 ms, so a Western Slope-sized company will be slower. Without the changes, the estimate is 4–6 s to the first word on a question that needs a lookup (inference).
Seam choice (engine-seam findings, origin/main a3a839f1a0):
| A. One agent loop | B. Ask Clara loop | C. Tavus runs the model, calls our tools | |
|---|---|---|---|
| Streams today | No: handleIncomingMessage returns Promise<string>; every call is messages.create | Yes | Yes (vendor) |
| Staff tools and write gate | Yes: desk, metrics, mass comms, stripStaffTurnWriteTools | No: 23 reads, its own prompt | Redeclared to Tavus |
| Actor | Phone or email today; needs a person key | Signed-in user | Phone |
| Verdict | Chosen | Second brain | Second prompt family; staff data to the vendor's model |
ChannelAdapter, not SessionChannelAdapter, because we run the model on every turn. parseInbound checks the bearer and conversation-id, loads the #15301 binding (fail closed), takes only the last user message, and emits channel:'video', carrier:'tavus', sender video:<tavusId>. It calls handleIncomingMessage directly, like clarifyForStaff (reply-release.ts:2233), with senderRole:'pm', staffTiers, pmAuthorId, lineOrganizationId, onTextDelta, abortSignal and readOnly. Delivery is the response stream; sendOutbound skips. A new turn aborts the one in flight. Dedupe on (conversation id, message count, last-text hash).stripStaffTurnWriteTools) does not make a video turn read-only. It only guards staff questions inside a resident's escalation thread. A video staff turn carries staffTiers, so it gets the desk write tools. v1 adds its own gate for channel:'video': an allowlist of read tools, checked at composition and again at dispatch, with a mutation control.resolveStaffActorByPerson(personId, orgId), because resolveActor only takes a phone or email. Refuse the turn if the person holds no active staff role in that org.dashboardChatTools executed by the existing handleDashboardChatTool, with ctx from a new toolContextForStaffActor. For an SMS staff sender with no login, a reviewer must check canViewProperty, which relies on user.senderRole:'pm' (weekday guard, hallucination swap, anomaly gate, same-turn escalation hold reason:'pm_sender', policy gate, fee guard, turnover guards); outbound safety runs but all three policies skip for audience:'pm'. Still running: strips and PII (per sentence safe; the internal-prompt strip is per paragraph) and draft salvage (needs final + pre-tool draft, so video releases text only after the last tool round). The handler today splits a finished answer with deltasOf; the engine must emit text after tool completion via onTextDelta. Wasted pre-model reads for staff: getToursByPersonId(staffPersonId) at conversation-manager.ts ~10445 and getOrgOperatingModel('') at ~10859.resolveDraftParams(channel) gives email and SMS Sonnet 5. Video takes the voice branch (AGENT_MODEL, Sonnet 4.6, effort low).turnScope:'pm_query'), not merged into the person's texting thread. Our rows are the transcript.'video' and 'tavus' plus VIDEO_CAPABILITIES (spoken, sync); widen 19 files. Delete the dead 'dashboard' member (envelope.ts:43). Its adapter ran through this same loop until #3994.proporg-index missing on dev, which breaks the tours board.What the answer line must handle (seen in the local proof's traffic):
| Seen | Handling |
|---|---|
conversation-id header on every turn (72 of 72). Not in Tavus docs, so it could change | No id or unknown id means refuse |
A setup-change test call with id tavus-openai-compat-test | Short canned answer with no data; never a 401 |
| Tavus adds 3 system messages per turn | Ignore them; our engine builds the prompt |
A perception block <user_audio_analysis> inside the user message | Strip it |
Speaker prefix such as [fede] | Keep it as the speaker name for meeting notes; never use it for identity |
| Speculative starts; read timeout 30 s (10 s on an older client) | Stop when Tavus drops the request; keepalive every 2–3 s |
Proposed — pending review. Fede asked for a version that runs entirely on Tavus (their recommended model, tavus-gemma-4, plus their knowledge base), to see if our own brain is the ceiling. Same face, same voice (Belle). The Tavus version reads a made-up company; ours reads Western Slope. Measured live in Tavus's own room: time from question to her first word (typed question, so microphone pickup isn't included for either).
| Tavus-hosted | Ours | |
|---|---|---|
| First word, median | 1.1 s | 4.5 s |
| First word, slowest (p90) | 1.2 s | 5.9 s |
| Answer length | 9 words | 18 words |
| Correct on fact questions | 1 of 6 (counts wrong: said 8 when 16, said 12 when 4; named a tenant and a "leaking pipe at 1242 Maple Street" that don't exist) | December count right (8); others look right, not yet checked line by line |
| Didn't make up what it lacked (prices, vacancies) | Yes, 2 of 2 | n/a (has live data) |
| Questions run | 10 | 5 |
What it shows: the speed gap is the brain, not Tavus. Tavus's own model starts talking in about 1 second. But its knowledge base looks up passages; it can't count or list, so it guesses, and it guessed wrong on 5 of 6 facts. It also has none of our live lookups, note-taking or privacy checks.
Found while testing: Tavus's fastest lookup mode ("speed") makes the hosted brain go silent; "balanced" works. That goes on the Tavus question list.
| Option | Speed | Correct | |
|---|---|---|---|
| A. Keep ours, keep cutting time | 4.5 s today | Yes | |
| B. Tavus brain talks, asks our server for every number (one tool call) | ~1 s for chat, slower for facts | Yes, if it always uses the tool | Recommended next test |
| C. Tavus brain only | 1.1 s | No (5 of 6 facts wrong) |
Built and off everywhere: company setting clara.videoBrain (default ours). Tavus-hosted test persona p2c6a736c5c4, made-up data only.
Proposed — pending review. Fede asked Oct 6: "an audit ... with the tools it has access to and what's missing. I want the entire PropFlow platform included ... Long term I'd like it to be able to access every single module's data and all entities." Read from the code on main, Oct 6.
Call start ──► summary of "what's happening now" (built once, free on every turn)
Each turn ───► 22 lookup tools (read-only) + live AppFolio reports
each lookup adds ~2–6 s to that answer
| Area | Record types | Covered | Partly | Can't see |
|---|---|---|---|---|
| Leasing | 8 | 1 | 4 | 3 |
| Residents | 12 | 3 | 3 | 6 |
| Money | 11 | 1 | 7 | 2 (+1 unverified) |
| Maintenance | 9 | 2 | 4 | 3 |
| Communications | 10 | 0 | 2 | 8 |
| Properties and company | 7 | 2 | 0 | 5 |
| Integrations and analytics | 8 | 5 | 0 | 3 |
| Total | 65 | 14 | 20 | 30 |
What it shows: every existing Ask Clara lookup is already on video, so new reach means new lookups, not switching old ones on. Biggest blind spots: the knowledge base (pets, parking, policies), households, renewal offer details, payments received, the calendar and waitlist, and inspections. She can already pull live AppFolio reports (delinquency with late counts, move-out reasons, ledgers), but slowly and only for AppFolio companies.
Constraint: her call-start summary takes 19–31 s to build against a 30 s limit, so most new data has to be lookups, not more summary.
Decided Oct 6 (Fede): narrower than the list below. Build: move-out reason, who's behind on rent, lease terms. Then all the basics for leasing (prospects, tours, application status), maintenance (how many open, any ticket's details) and renewals: counts in her call-start summary, details through the lookups we already have. Not now: turnovers, building knowledge, the rest of the platform.
| # | Add | How | Why |
|---|---|---|---|
| 1 | Move-out reason | Keep the reason from the AppFolio notice email (today it's read and thrown away); show it next to each notice | Fede asked in the meeting; "why are people leaving" |
| 2 | Who's behind on rent | A past-due lookup (names and how late, from the balance history we already store) | Most common money question |
| 3 | Rent and lease terms per tenant | Nothing to build: a prompt line telling her to use the lease lookups she already has | Cheapest win |
| 4 | Turnover pipeline | Unit, stage and ready date in the summary | The follow-up to every move-out question |
| 5 | Building knowledge (pets, parking, hours, policies) | A knowledge-base lookup | She can't answer policy questions at all |
| 6 | Household members | A household lookup (no phone or email) | Notices and renewals often name co-tenants |
| 7 | Renewal offer detail and change requests | A lookup on the renewal reviews | "What did we offer, what did they ask for" |
| 8 | Collections and payments received | Lookups on collection reviews and payment history | Follows #2 |
| 9 | Vacant and notice-given units | A tiny summary block (units and ready dates only) | Leasing and move-out planning |
| 10 | Calendar, waitlist, held emails, escalations | Read lookups per module | Completes leasing and inbox |
Long-term shape (toward "every module"): a thin summary of what's happening now, plus one read-only lookup per module, each checked as read-only before it's allowed on video. Kept out on purpose: login credentials, phone-line setup, staff accounts and sync internals.
| Option | |
|---|---|
| A. Build #1–#5 now, then one lookup per module in the order above | Recommended |
| B. Build only #1–#3 (the meeting gaps) and stop | |
| C. Build a single "read any record" lookup across all modules | Fastest coverage; harder to keep read-only and quick |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Prospects, applications (stage) | yes (active, names, stage, source, move-in) | list_prospects, get_prospect, find_guest_card | none; closed/lost prospects only by tool; no phone/email by design | easy |
| Guest cards / inquiries | no | find_guest_card (one at a time) | partial: no list, no inquiry history | medium |
| Tours | yes (+/-14 d) | list_tours, check_tour_status, get_tour_notes | partial: tour outcomes/no-show history only per tour | easy |
| Tour availability / calendar slots | no | none | none at all (calendar module, tour-day policies) | medium |
| Waitlist | no | none | none at all | medium |
| Listings, public feeds, unit prices, concessions | no (by design) | list_units (rent), get_market_context | partial: no listing text, price history, concessions | medium |
| Leasing settings (sources, hours, intake mode) | no | none | none at all | medium |
| Leasing conversations | no | list_conversations, get_conversation | partial: fine via tool | easy |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Tenants / occupancy | partial (only names in lease-end and move-out lines) | list_tenants | partial: no roster, no household members | easy |
| Households, people, roles | no | none | none at all (person spine, household, merge audit) | medium |
| Leases | partial (end date only) | list_leases, list_tenants | partial: no rent/term in snapshot (by design) | easy |
| Lease end counts, month-to-month | yes | list_leases, list_renewals | none | easy |
| Renewals (state) | yes (90 d + counts of all rows) | list_renewals | none; offer terms only by tool | easy |
| Renewal offers, change reviews, packages (new-lease) | no | none | none at all (renewal/new-lease change reviews, package reviews) | medium |
| Notices / NTV (date) | yes | list_tenants, appfolio_report (tenant_tickler) | none for dates | easy |
| Move-out REASON | no | appfolio_report tenant_tickler only (live, AppFolio only) | not stored in PropFlow: NTV email parser reads "Move Out Reason" and the ingester does not keep it; AppFolio sync maps it only for an admin preview. Snapshot says "our records hold no reason for leaving" | hard to store; easy-ish live via appfolio_report |
| Move-outs (completed) / turnovers | partial (notices only) | list_turnovers | partial: no list of units turning over in snapshot | easy |
| Condition reports (move-in/out) | no | none | none at all | medium |
| Key pickup bookings | no | none | none at all (NV) | medium |
| Tenant confirmations | no | none | none at all | medium |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Balances | no (by design) | list_tenants, get_tenant_balance_history | partial: aged buckets and last payment exist per snapshot | easy |
| Late-payment flag | no | list_tenants with name (balance over 30 days, last payment); appfolio_report delinquency (late count, plan, collections) | no stored "paid late" flag or late-count history in PropFlow; only aged snapshots | hard to store; workable live |
| Delinquency / collections list | no | appfolio_report delinquency | partial: AppFolio companies only, slow (live paged read) | easy (live) / medium (stored) |
| Collections reviews (dun), cash-assistance inquiries | no | none | none at all | medium |
| Payments received (monthly receipts, tender mix) | no | none | none at all (payment history stored) | medium |
| Charges, tenant ledger | no | appfolio_report tenant_ledger | partial: live only | easy (live) |
| Financial periods, NOI | no | list_financial_periods | none (tool) | easy |
| Unit revenue periods, rent snapshots (loss-to-lease) | no | get_portfolio_insights (indirect) | partial | medium |
| Budgets | NV | none | NV: no budget type seen in the type list | hard / NV |
| Bills (payables), GL | no | appfolio_report bills, general_ledger, income_statement | partial: live only | easy (live) |
| Purchase orders, vendor quotes/invoices | no | get_work_order (quotes) | partial: no PO list | medium |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Work orders (open, recent closed) | yes | list_work_orders, get_work_order | none | easy |
| Work order notes, photos, timeline, line items | no | get_work_order | partial: one at a time | easy |
| Vendors | partial (vendor name per work order) | list_vendors | none for roster; no vendor performance, preferred lists | medium |
| Vendor calls, scheduled visits, contact points | no | none | none at all | medium |
| Inspections / condition reports | no | none | none at all | medium |
| Turnovers (tasks, charges, timeline) | no | list_turnovers | partial: task detail missing | medium |
| Appliances | no | list_appliances (one unit) | partial: no building-wide view | easy |
| Maintenance manuals / routine tasks | no | list_maintenance_manuals | none | easy |
| Maintenance settings | no | none | none at all | medium |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Conversations, messages | no | list_conversations, get_conversation | partial: tenant-by-person lookup not offered | easy |
| Held emails / review queue | no | none | none at all (review page) | medium |
| Email ingestion records | no | none | none at all | medium |
| Calls (recordings, analysis) | no | get_conversation (transcript) | partial: call analysis NV | medium |
| Notes (staff / unknown-caller notes) | no | get_tour_notes only | none for general notes | medium |
| Knowledge base (property knowledge) | no | none | none at all (getPropertyKnowledge exists) | medium |
| Playbooks | no | none | none at all | medium |
| Mass sends / drafts and outcomes | no | none | none at all | medium |
| SMS status, consents, suppression | no | none | none at all (compliance lookups) | medium |
| Escalations (PM escalation actions), activity log | no | none | none at all | medium |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| Properties | partial (names only) | list_properties | none | easy |
| Units | no (by design) | list_units | none | easy |
| Property knowledge, preferred vendors, settings | no | none | none at all | medium |
| Organization, settings, enabled modules | no | none | none at all | medium |
| Users / staff / roles | no | none | none at all | medium |
| Onboarding record, go-live state | no | none | none at all | medium |
| Portfolio registry (phone lines, mailboxes, calendars, PMS credentials, settings, vendor rosters, schedules by org/group/property) | no | none | none at all | medium (and sensitive: credentials must stay out) |
| Entity | Snapshot | Tool | Gap | Difficulty |
|---|---|---|---|---|
| AppFolio sync health, sync logs | no | none | none at all | medium |
| AppFolio live reports (15 endpoints) | no | appfolio_report | covered, slow | easy |
| Portfolio insights | no | get_portfolio_insights | none | easy |
| Conversation themes | no | get_conversation_themes | none | easy |
| Metrics (catalog, history) | no | get_property_metrics, get_portfolio_stats | none | easy |
| Market data | no | get_market_context | none | easy |
| Agent traces, grading, evals, cost, spend | no | none | none at all (internal, low value for a PM) | medium |
| Reports / report data | no | none | none at all | medium |
Decided Oct 6 (Fede, after a live meeting test): "let's make this the permanent version and remove the feature flag and the old version... this becomes a long-term architecture." Our own brain is the only video brain. The hybrid and Tavus-hosted versions, and the company switch between them, are being deleted. In that meeting Clara answered in a median 1.8 s on our server across 27 turns.
Being fixed now, from the same meeting: (1) she said "Sorry, I couldn't get that" three times when Fede was talking to himself; she should stay quiet. (2) She listed October offers when asked about November; she should answer only what was asked. (3) Basic renewal and tenant questions: who gave notice and why, the renewal increase, how many renewals are open, a tenant's balance and late-payment history.
Proposed — pending review. Same 10 questions, same test company (a copy of Western Slope's buildings, no real customer on the call), same face and voice. "Hybrid" = Tavus's model talks and asks our server for every number (option B above). Answers scored against the real data, read with the row cap off.
| Ours, before (Oct 5) | Ours, after tonight's fixes | Hybrid | |
|---|---|---|---|
| First word, median | 4.5 s | 4.8 s | 2.2 s |
| First word, slowest (p90) | 5.9 s | 8.3 s | 3.4 s |
| Answer length | 18 words | 23 words | 11 words |
| Questions answered | 5 of 5 | 9 of 10 (no answer on the last one) | 10 of 10 |
| Facts right (8 checkable) | not scored | 6 of 8 | 5 of 8 |
| Stays quiet on team chatter | No (answered everything) | Yes (live check: 0 words, turn ran clean) | Not built yet |
| Question | True answer | Ours | Hybrid |
|---|---|---|---|
| Leases ending in December | 8 | ✅ 8 | ❌ 4 (read a cut-off list) |
| Who gave notice | 11 tenants | ❌ "no one" | ❌ named 1 of 11 |
| Renewals waiting on a decision | 75 | ❌ 59 | ❌ "none" |
| Open work orders · vacant units · 30+ days late · occupancy · cheapest vacant | 0 · 54 · 0 · 77% · $1,250 (checked on 200 of 234 units) | ✅ all five | ✅ all five |
What it shows: the hybrid starts talking twice as fast and is about as accurate. Why the misses happened: the hybrid's December miss was a cut-off list (verified: 27 KB of leases cut to 8 KB). Its renewals miss was a lookup that came back empty, likely a status-name mismatch (every renewal is marked "ready"). Our misses go through a different path; cause not checked yet.
Hybrid lookups: 8 calls in 10 questions, all succeeded. Our server took 0.3–2.0 s each (median 0.8 s), over the 0.3 s target; the slow ones are the big lists. Hybrid isn't yet told to stay quiet on chatter; that's its own small change. It still runs the instructions it was created with on Oct 5, not today's video instructions.
Ours, after: tonight's fixes were silence, screen share and counts, not speed. It is no faster, and the slowest answers were slower (8.3 s vs 5.9 s), on a different data set with more questions. "Before" was on the real Western Slope data, "after" on the test copy: compare speed, not facts.
| Next | |
|---|---|
| A. Count tools for lists (leases ending in a month, notices, renewals by status) | Recommended: fixes the December-style miss on both; renewals also needs the status names fixed |
| B. Hybrid stays quiet unless asked | Needed before hybrid can go into a meeting |
Proposed — pending review. Five minutes, Western Slope Clara in Fede's team Zoom. Sources: the Tavus transcript, our server logs for the call, and a read of the live renewals data.
| What went wrong | Why | Fix |
|---|---|---|
| Wrong voice (not Belle) | We set Belle on ElevenLabs' fastest engine, which Tavus doesn't list as supported | Done: moved to the engine Tavus documents, same Belle voice. Prove it on the next join. |
| Said "December has 6 renewals"; the true number is 8 | She had all 8 rows, but only an 18-lease total for the next 90 days. She counted the rows by hand and got it wrong. | Add a count per month to the facts she gets at call start |
| Then said Fede's 8 units "match what I have" | She agreed with the list read to her instead of noticing it contradicted her | Same fix; with real counts there is nothing to guess |
| Spoke up about room chatter ("none of that was for me, I'll stay quiet") | Tavus sends every bit of room chatter as a turn, and she announced she was staying silent | When she isn't addressed, she says nothing at all |
| "I only see a Zoom notification" while Fede shared the Renewals screen | Tavus's own screen check saw the Renewals page; her answer did not use it | Look at what reaches our server from Tavus's screen check |
| Replies cut short ("Yes," / "three oh") | About 40 turns from Tavus in 5 minutes for about 10 real questions; people talking over her cancelled 4 of them | Tavus turn settings: reply only when addressed by name in a group |
| Slow first reply (3.5 s), long answers | Joined from a script on Fede's Mac, which skips the warm-up. The shorter-answer fix is still in the merge queue. | Join through the normal invite path; merge the two shorter-answer PRs |
Speed on this call: first word after she was asked: 1.6–3.6 s on most turns, 4.2–5.6 s on 4 (cold start, a fresh server, one data lookup). No errors, no timeouts.
Proposed — pending review. Fede's framing, Oct 5: the face mostly mirrors what is said, so video Clara is a voice agent. We already know how to run voice agents on ElevenLabs. Treat Tavus as one more provider that adds a face. The only real difference is the audience: phone Clara talks to people outside (tenants, prospects, vendors), video Clara talks to people inside (coworkers).
The flaw today: phone and video are two different machines. Phone runs on ElevenLabs with rules written for calls. Video runs on Tavus with our email and text rules plus one line asking her to be short. That is why she read seven renewals aloud and spoke her private notes on the Oct 5 Zoom test.
| A. Keep Tavus running the call | B. ElevenLabs runs every voice, Tavus only draws the face | C. Build our own voice pipeline | |
|---|---|---|---|
| What changes | Video takes the shared call rules (in flight now) | Add a coworker specialist on ElevenLabs; send its audio to Tavus Echo Mode, or to a voice-only meeting bot | LiveKit or Pipecat on our servers; face as a plug-in |
| Speed | Stuck at ~3.6 s until Tavus speaks sentence by sentence | Phone is 1.8–2.3 s today; video inherits that plus the face (estimate) | Best possible; most work |
| One set of learnings | No: two turn-taking setups, two voices | Yes: every phone fix helps meetings too | Yes, but we leave ElevenLabs |
| Risk | Low | Echo Mode in Zoom is unproven; staff data goes to ElevenLabs (tenant data already does) | High; months |
Recommendation: B, in three steps.
c67ab293976cc446. If Echo can't join Zoom, the fallback is a voice-only meeting bot.Unverified: whether Echo Mode works with a meeting link (docs show it in Tavus's own rooms); whether ElevenLabs agents can join meetings natively (none found; the documented route is a meeting bot). Phone's model runs at ElevenLabs today, so B also means company-level staff data reaching ElevenLabs.
Proposed — pending review. Measured on two test calls the night of Oct 4–5; nothing below is being built until Fede picks.
Short answer: the model is the biggest single piece (1.2 s, measured). The likely surprise: on 12 of 12 turns, Clara's voice started 0.57–0.73 s after our whole reply had arrived, never during it. That points to Tavus waiting for the full reply (~0.7 s per answer), but it is not proven: every reply was about the same length (~100 tokens), so a slow voice engine would look the same. The 3-second pause test in move 1 settles it.
What we already did (Oct 4–5)
| Measure | Before | After |
|---|---|---|
| First answer of the call | 4.9 s | 3.3 s |
| Typical answer | 3.7 s | 3.6 s |
| Slow answers (p90) | 5.6 s | 4.8 s |
Tavus test minutes used: 6.04.
Top 3 moves
| # | Move | What it hits | Saves (estimate) |
|---|---|---|---|
| 1 | Make Tavus speak as soon as the first sentence arrives. Ask Tavus, and run one cheap test: a reply that sends one sentence, pauses 3 s, then sends a second, once with emotion control on and once off. | The ~0.7 s Tavus spends waiting for our full reply | ~0.7 s |
| 2 | Shorter video answers (~40 tokens instead of ~100). A wording change, so Fede decides. | The same wait, while Tavus still waits for the full reply | ~0.35 s |
| 3 | Tavus settings: turn-taking patience low, plus their faster voice engine. | Turn detection and voice start | ~0.4–0.6 s |
Instant opener (~1.9 s off the first sound) only works once move 1 is fixed; today Tavus would hold the opener until the whole answer finished.
Ruled out
Decisions for Fede
| Question | Options | Recommendation |
|---|---|---|
| (a) Ask Tavus, and run the 2-turn sentence-pause test (~3 Tavus minutes)? | Yes / Hold | Yes |
| (b) Shorter video answers (~40 tokens)? | Yes / No | Yes |
| (c) Tavus patience low + faster voice, one change at a time? | Yes / Hold | Yes |
| (d) Instant opener? | Decide after move 1 | Wait for move 1 |
Risk on (c): low patience may cut people off mid-sentence in meetings. The wake word "Clara" softens it, because she only answers when addressed.
Short answer: data isn't the delay. On the test link all of Western Slope's data is already loaded into her instructions when the call starts, so a normal question reads nothing (0 ms), and her words stream to Tavus as they're written. That's the same "everything in the prompt" approach the phone agent uses, with the same model. The extra time is on the video side: Tavus deciding you've stopped talking, an extra internet hop to our server, and rendering the face.
What Fede saw on the 8:45 and 8:51 calls (2026-10-02), and what changed the same morning:
| Problem | Cause | Fix on the test link |
|---|---|---|
| She repeated "two tours this weekend" to six unrelated remarks | She treated room chatter as a question and fell back to the last answer | New rule: answer only the newest thing said; side chatter gets a few words. Re-tested: "she's very robotic" now gets "ha, fair" |
| Three answers took 24–43 s, then "I hit a snag" | The model call stalled and nothing cut it off | No first word in 4 s: cancel and try once more, then a short "give me a second" |
| First question of every call about 3–4 s | Her loaded data isn't cached yet on the first question | The data is warmed the moment the call starts |
| Slow to answer after you stop | Tavus waited longer for a pause, and its "start answering early" setting was off | Quicker turn-taking, and early answering on. Tavus cancels an early answer if you keep talking, and we now stop it at once |
| Hard to cut her off | Interruption sensitivity was low | Raised to medium |
| Lips lag the voice | Not ours to see. The face is already Tavus's newest model (Phoenix 4.5) | Control call below |
Where data should live, as companies grow:
| Layer | What | Speed |
|---|---|---|
| Loaded when the call starts | A summary of the caller's own buildings: key numbers, listings, tours, prospects. Built in production by the call-start summary (#15487), limited to the caller's buildings (#15489). Western Slope is about 5k characters | 0 ms per question |
| Looked up mid-call | Rare deep questions, such as one tenant's ledger. Read-only lookups limited to the caller's buildings | One extra model round, about 1–2 s |
| Document search (RAG) | For long documents (leases, policy manuals). That's what ElevenLabs' knowledge base is. Not needed for numbers, and our knowledge base still fits in the prompt | Later, if documents get big |
This is the scalable version of "load everything into the prompt": load everything about this caller's buildings, cached, and keep lookups for the long tail. A 10k-unit company still gives each manager a small summary because it's scoped to their buildings.
Proposed — pending review. In production, Clara writes the whole answer before Tavus starts speaking. Streaming it word by word is a chain of three changes, not a small one: (1) a streaming version of the shared model call that every Clara channel uses, (2) a decision on three safety re-checks that today re-write a finished answer (they can't re-write words already spoken), (3) a rule for the two steps that clean an answer before it goes out (removing internal instructions, and the personal-information check). Options:
One Clara brain, on our side. Tavus (and later the phone vendor) only carries the face and voice. Speed comes from streaming, a warm prompt cache, running in the cloud next to Tavus, and trimming setup.
Ruling (Fede, 2026-10-02): no smaller (Haiku) model, and no Tavus-hosted brain with customer data. The Tavus-hosted model is used only as an A/B measurement, against a made-up company ("Pinecrest"), so no customer data goes to Tavus.
| Website call | Meeting listener | |
|---|---|---|
| Who starts it | A signed-in person clicks "Talk to Clara" | A signed-in person schedules Clara into a meeting link |
| Company comes from | That person's sign-in | The person who scheduled her, never the meeting itself |
| She speaks | Every turn | Only when addressed ("Hey Clara"), using Tavus's wake and sleep phrases |
| Afterwards | Nothing extra | The transcript goes to renewals as notes waiting for approval |
Staying on the $59 plan: Clara rejoins every ~15 minutes. Starter caps each Tavus call at 15 minutes, and a renewals meeting runs longer. Whether Starter can join meetings at all is unverified: the pricing page and the meetings doc don't say which plans can.
VIDEOMEETING#<id> {organizationId, schedulerUserId, meetingUrl, startsAt, children[]}, and each child is a VIDEOCALL#<tavusId> binding with parentSessionId. Children are created only by our server.meeting_url, max_call_duration about 870 s, and a per-session callback_url secret. Perception off and wake_phrase/sleep_phrase live on the setup (the PAL's conversational-flow layer), not on the call.This is the most dangerous part. One rule: only a signed-in person starting Clara from our app names the company. Every other signal can only make her say less.
| Signal | Can it name the company? | Why |
|---|---|---|
| Signed-in start in our app, membership checked | Yes, the only one | Forging it means stealing a session |
| Meeting claimed by that company at start | Only because of the start | Written once; can't be claimed twice |
| The company's calendar got the invite | No | Anyone can invite a company's mailbox |
| Organizer email | Check only | Spoofable without a passing email check |
| Attendee email domains | Veto only | Anyone can add attendees; many managers use gmail |
| Meeting title, display names | Never | Typed by anyone; a place to plant instructions |
| Case | What Clara does |
|---|---|
| Attendees from another client, or unknown people | Company stays pinned; the whole call switches to no specifics (no names, balances, phones or emails) until they leave |
| A person who belongs to two companies | Company comes from the start, never from the person |
| PropFlow staff start it | Must pick one company, with a reason, audited; "all companies" refused; no "view as" |
| Invite forwarded, or link reused | No new binding, so no data |
| "Do you also work with Situs?" | One fixed line: "I can only speak to [Company]'s portfolio in this meeting." |
| No binding, expired, or broken | No data, and she says "I'm not connected to a company for this meeting." |
What Tavus sees: we upload nothing to Tavus. It hears what Clara says on the call. Recording and memory are off, and each conversation is deleted after the call.
v1 (next week): start from our app only; the client's own staff only, because Tavus docs don't say what attendee or invite details Clara sees, so she can't detect outsiders yet; read-only; the start page warns that Clara will share this company's data with everyone in the meeting. Later: joining from a calendar invite (with a verified organizer and the attendee veto), automatic no-specifics mode, actions, and owner meetings.
| # | Threat | Defense |
|---|---|---|
| 1 | "Ignore previous instructions," spoken or typed | Company never comes from model text or the Tavus request; tools can't reach other companies; no write tools in v1 |
| 2 | Instructions hidden in data (an email, a work-order note) | Same wall; no raw content read aloud |
| 3 | An outsider invites a company's calendar | v1 never starts from an invite |
| 4 | Forwarded invite or reused link | No new binding; unknown joiners mean no specifics |
| 5 | Company picked from a person in several companies | Named at start; no person id on the call's viewer |
| 6 | Staff "all companies" binding | Refused for meetings |
| 7 | Stolen call id (it's in the room link) plus the one shared Tavus key | Bindings live ~18 minutes; rotate the key; per-call key if Tavus allows |
| 8 | Notes written into the wrong company's renewals | Writes through the pinned company and its building list; only the starter approves |
| 9 | Tavus hears the answers | Listed as a data processor; conversations hard-deleted after each call |
| 10 | Recordings | Tavus recording off; the client's own Zoom recording is outside our control, another reason for no specifics |
| 11 | Tavus memory carrying one client's facts to another | Memory and knowledge base off; Tavus history untrusted every turn |
| 12 | Logs or error reports holding meeting speech | Ids only; check whether Sentry captures request bodies |
| 13 | Asked about another client by name | One fixed line; no other customer names in the prompt |
| 14 | Answers changing mid-call | Buildings fixed at start |
| 15 | A broken binding falling back to wide scope | Every failure means no data |
VIDEOCALL#<id>/binding via recordVideoCallBinding (#15301), write-once, read consistently, expiring. For meetings, refuse when getUserOrgScope(user) === null. Write it in the scope-row shape mintFromConversation reads (organizationId, scope key, propertyId, mapVersion), so moving to getRepository/checkPartition later is cheap.buildVideoToolContext (llm branch) builds a stand-in viewer with no person id, cut to the bound company's buildings. Never use requireUser, getUserOrganizationId or resolveInboundStaffMembership* here: without an org they return the first company found.isForeignOrg/fenceIdentityToOrg: they fail open on unknown companies, on purpose, for texting. Meetings need fail-closed.property_id over the server's (/api/voice/tools/[tool] :428–433). Video must not copy that.Source: isolation findings, 2026-10-01 night.
Clara is the ears; the renewals team owns what happens to what she hears.
Proposal from the renewals lane (to be decided with Fede after Western Slope's renewals turn on Monday; date unverified here): no separate store. The notes become a per-lease, per-renewal-cycle override that the renewal policy reads, keyed the same way as the renewal record (lease plus cycle).
Source of real meetings: we have no Camellia tenant-by-tenant renewal-meeting transcripts. Western Slope holds a weekly recorded renewals meeting and offered to invite us (unverified here).
RenewalSaga as its read-model (ADR-0047, renewal-orchestration runbook). No notes or human-context field exists on Tenant, Lease, Renewal, RenewalSaga or RenewalSnapshot. The approve route is /api/leasing/renewals/approve (approve / reject / convert_mtm); HUMAN_REVIEW_PENDING maps to escalated.{leaseId, cycle, termMonths?, proposedRent?, exception?, perPerson?: [{personId, note}], source: {meetingSessionId, quoteStartSec, quoteText}, status: proposed|approved|rejected, approvedBy?}."Send this renewal, but make this exception" goes through the same staff tools and the same write gate as a staff text. Clara repeats the action back and waits for a spoken yes before doing it.
Honest gap: staff texts today have no built-in "confirm before write" step. Desk writes run as soon as the model calls them, and the prompt only asks Clara to read back the result (staff-write-gate.ts, staff-leasing-desk-prompt.ts). We build that confirm step once, in the shared staff capabilities, for text and video together. A video-only version would be a parallel path.
Video Clara is run like the phone agents: her setup lives in our code, a merge deploys it, and a nightly check catches hand edits. The setup Tavus holds is thin and has no client data; what Tavus hears on a call is covered under Keeping companies apart. Everything about which company is on the call lives on our server.
| Piece | Phone today (fact) | Video (proposal) |
|---|---|---|
| How many setups | One shared set for every client (Fede, 2026-09-05/06; Western Slope row 2026-09-11) | One thin Tavus setup per environment (dev and production), shared by every client |
| What's in it | Prompt, tools and turn settings per agent | Stock face, Clara's ElevenLabs voice, speech engine, turn-taking, perception off, and our answer address and key. The prompt is a stub; the real one stays in our engine. |
| Source of truth | Agent modules + config/elevenlabs-agents.json, 7 sync scripts | One file (config/tavus-personas.json), one sync script, one comparator |
| Deploy | On merge only, behind deployLaneRefusal(); "THERE IS NO BYPASS" | Same lock. The PR shows a dry-run diff; the merge applies add/replace/remove changes, reads back, and restores the snapshot on mismatch |
| Drift | Live check on PRs; nightly report | Same, report-only |
| Testing before merge | Drift tests, evals, fastlane real call | A text runner through the one Clara engine against Fairhaven on every PR (no Tavus minutes), plus one real call nightly |
| Staging | Willows clone fleet, same account; voice has no preview lane | The dev setup, pushable from a branch. Tavus has no publish step, so dev is the only buffer. |
| New client | No new agent; a company record and a phone row | No new setup; the company comes from the call binding |
Two rules that come from how Tavus behaves:
The Western Slope production snapshot used by the local proof never goes into git or CI. Tavus docs put the wake phrase on the setup, and create-conversation lists no per-call override, so meeting mode likely needs a second setup per environment (inference). Source: build-deploy findings, 2026-10-01, from origin/main e6be230d12.
The code is done. Clara can take a video call, answer from the same brain as text, email and phone, look things up only inside the caller's buildings, and close the call cleanly. Nothing is on at any company yet.
| Piece | Status |
|---|---|
| Staff questions stop loading the prospect leasing instructions (#15322) | Live |
| Video is a channel of the one Clara brain, Tavus is its carrier (#15319, #15359) | Live |
| Start a call and tie it to the signed-in person's company (#15301) | Live, off until the company setting is on |
| Know who is on the call and what they may see (#15330) | Live |
| Portfolio lookups for staff, on text too (#15338) | Live everywhere, no switch (Fede's yes) |
| Video turns can only read, never act (#15342) | Live |
| The endpoint Tavus calls for each answer (#15365) | Live, needs the production secret |
| Lookups stay inside the call's buildings (#15489) | Live |
| A summary of the caller's buildings ready when the call starts (#15487) | Live |
| End of call closes the conversation; late answers are refused (#15484) | Live |
| The page to start a video call (#15483) | Live, off until the company setting is on |
| Admin company picker on the video call page | Merged. Platform admins choose the company and give a reason before a call |
| Ending a call, removing Clara from a meeting (#17579) | Merged 2026-10-07 (main 45b6f592e). Found in Fede's Zoom test: "Done" and "End Call" only cleared the page and never ended the Tavus call. Now both end it; meeting mode gets a "Remove Clara from meeting" button. Not a fix: Clara saw a black feed with a Zoom notice until the testers rejoined; our code sets nothing about vision, so that came from the Zoom side |
| Meetings Clara | Set up for testing. Joins Zoom, stays quiet until "Hey Clara", goes quiet at "Thanks Clara" |
| A/B test page | Ready. Same face and voice, our brain vs Tavus's own model, on a made-up company |
| Minute protection | Done. A call nobody joins ends after 1 minute; calls end 1 minute after the last person leaves the web call, 30 seconds after a Zoom empties |
| Plan | 175 video minutes a month, pay as you go after that, 3 meeting seats |
| Pronunciation list for video | Not yet. The phone has one; video needs its own |
Streaming, reopened 2026-10-02. Most whole-reply checks are off for staff, but three re-checks that re-write a finished answer and the two cleaning steps still run, so true word-by-word streaming is a chain of three changes. The plan is in Why video feels slower than the phone.
Skipped for now: the full staff write list (no exposure: video can only read).
What's left, in order:
Follow-ups (not blocking): a "Talk on video" link in the app, a company picker so admins can start a call, the outcome event for company-level calls, a race when the call ends mid-answer, and the eval proof for the call-start summary (#15488).
Open, for Fede:
| Question | Recommendation |
|---|---|
| Buy Tavus Starter ($59 a month, 15-minute calls; rejoin covers long meetings)? | Yes. A purchase, so Fede's go. |
| Apply to Recall.ai's startup program, for the long-term listen-mostly option? | Yes |
| Should staff texting Clara get the same portfolio reads (read-only, staff-only, no switch)? | Yes. Same tools on every modality. |
| Perception off for meetings? (A proof call described the caller's face.) | Yes, off |
Tavus's AI notice (disclosure_type) in client meetings | Needs a legal check before any client meeting |
Decided by Fede on 2026-10-01: Clara's phone voice (ElevenLabs), a Tavus stock face with no real person, Tavus account set up, answers only in v1, website first. Updated the same night: also join a client's renewals meeting next week. Video is another modality of the one Clara, not a wrapper around Ask Clara.
leasingBodyWouldLoad, clara-unified.ts:85–111). This is likely live on today's staff texting desk too (inference; one desk trace will confirm). It's chain step 1, a fix with no switch.| Option | Cost | Good | Bad |
|---|---|---|---|
Tavus audio-only (audio_only: true) | Not published; assume the same per minute | No code change | Same 15-minute cap assumed; no saving known |
| Meeting-bot service (Recall.ai) + our ElevenLabs voice | $0.50 an hour + $0.15 an hour for transcription, about 1¢ a minute | Zoom, Meet, Teams; speaker names; no length cap | More for us to build |
| Phone Clara dials the meeting's dial-in number | Existing phone costs | Reuses the phone agent | Needs dial-in on their account; no speaker names; shows up as a phone number |
Sources: Tavus create-conversation docs; recall.ai/pricing. Recommendation: Tavus with rejoin for next week; a meeting-bot service is the long-term option for meetings where Clara mostly listens.
Research passes 1, 2 and 2b (2026-10-01), covering part of the ~230 doc pages. More research is coming and will be added to this section.
| Piece | What it is |
|---|---|
| PAL (was "persona") | The setup: model address, voice, speech engine, perception, turn-taking |
| Face (was "replica") | Rendered by Phoenix (4.5 since Sept); 50 stock faces |
| Raven-1 | Perception: sees the camera and screen share; sends what it sees to the model |
| Sparrow-2 | Turn-taking (default since 2026-09-08) |
| Custom model | OpenAI-style streaming endpoint; per-PAL headers and extra body; speculative starts before the speaker finishes |
| Voice | ElevenLabs or Cartesia with our key and voice id |
| Speech-to-text | Theirs only (auto, Soniox, Whisper, Deepgram); hotwords for hard names |
| Setting | What it does |
|---|---|
meeting_url | Joins Zoom, Meet or Teams (Teams since 2026-09-12) |
wake_phrase / sleep_phrase | Silent until addressed, keeps listening for context (set on the setup, not per call) |
idle_engagement | off / patient / eager re-engagement when people go quiet |
pal_interruptibility, turn_taking_patience, voice_isolation | How easily she is cut off, how long she waits, background-voice filtering |
callback_url | End-of-call events, including the transcript with speaker names; no signature documented |
audio_only, test_mode | Voice-only call; a call where the face never joins (whether it skips the concurrency limit is unverified) |
| Tool delivery by API | Signed with HMAC-SHA256 (X-Tavus-Signature) |
| Plan | Price | Minutes | Per call | At once |
|---|---|---|---|---|
| Free | $0 | 25 | 5 min | 1 |
| Starter | $59 a month | 100 | 15 min | 3 |
| Growth | $397 a month | 1,250 | 60 min | 10 |
Overage is $0.37 a minute ($0.32 on Growth), billed with a 30-second minimum and then in 6-second steps. Third-party comparisons put Anam at $0.11–0.16 a minute. Sources: tavus.io/pricing, docs.tavus.io (llm, meetings, conversational-flow, webhooks, create-conversation, tools), research file pass 2.
Not documented yet: the bot's display name, waiting rooms and passcodes, what ends a meeting call, how speakers are told apart, the attendee list, and API rate limits.
Not yet: taking actions such as booking a tour or adding a note. See Actions on a call.
See the plan table under Tavus: everything we learned. With rejoin, a one-hour meeting is four Starter calls, about 60 of Starter's 100 monthly minutes. Buying a plan is Fede's call.
Vision — Fede, 2026-10-02. Not being built; written down so today's design choices don't block it.
Each customer gets a company brain that Clara draws on.
Clara checks this layer before acting. Example: renewals. Today the renewal flow follows basic rules. With the brain, Clara first checks whether a team meeting or decision changes what she should do.
It is an add-on, so not every customer will have it.
What it means for today's video work: meeting transcripts are worth keeping per customer rather than throwing away, and they never cross between customers.
Tracking issue: #15255. Sources for this page: origin/main dfeb59b5cc (inbound-router.ts, envelope.ts, conversation-manager.ts, capabilities/index.ts, dashboard/chat), ADR-0022, ADR-0054, architecture-source-of-truth (P5, P8, F7), portfolio-architecture-how (§02, §04, §05), agent-loop-decoupling-2026-09, voice-agent-architecture-decision, docs/runbooks/elevenlabs-voice-agents.md, docs/runbooks/renewal-orchestration.md, and the local proof log.