ADR-0097: Bland calling and a public receptionist¶
- Status: Accepted; owner approved implementation on 2026-09-11 as Milestone 27 (amended by ADR-0108, 2026-09-19)
- Date: 2026-09-11
- Related: engineering plan Sections 2.5, 21 (roadmap B11), 22, and 29; ADR-0017, ADR-0064, ADR-0071, ADR-0082, ADR-0090
- Proposal: Bland calling
- Owner intent: make and receive telephone calls using the owner's existing Bland account; anyone may call the assistant; an inbound number is unconfirmed
Context¶
The owner explicitly requested Bland calling. Public call reception differs from the existing paired Telegram and WhatsApp channels: an unknown correspondent must be able to leave a message, without becoming an authenticated owner or receiving the owner's private context and tool authority.
The canonical plan still lists voice in roadmap B11. Its roadmap-entry rule requires owner authorization and a specification with gates. Before this decision, no telephony workstream existed in project state. The inbound-surface contract also forbids content-bearing writes from unpaired senders. Implementing public calling as an ordinary paired surface would contradict that contract. This ADR authorizes an explicit, separate correspondence boundary under Milestone 27.
Decisions¶
- Bland owns the live voice conversation. Veetbot supplies an approved outbound brief or an owner-curated public receptionist profile and receives attributable call results. This does not replace Veetbot's model gateway or route each spoken turn through the durable run loop.
- Public reception has no owner authority. Authenticated provider call records may be stored for the principal bound to the configured account and number. The caller is an external correspondent, not a paired user. Caller ID is not authentication. There is no call-triggered run on the caller's behalf, private-context upload, live Veetbot tool bridge, or automatic memory source admission. Existing paired channels retain their current rules.
- Outbound approval binds the brief and disclosure. The owner sees the actual number, task, permitted facts, duration, recording choice, and agent revision. Every dispatch uses the existing external-message approval and non-idempotent effect lifecycle. Generated speech is not an exact approved script; binding commitments are outside this initial scope.
- Narrow first-party REST integration through MCP. Read and call modes have distinct, fixed rosters and honest side-effect classifications. The remote provider's general mutation and account-administration tools are not exposed to the model. Provider types remain outside the core.
- Signed callbacks are receipts, not instructions. Restricted ingress verifies the bounded signed body before parsing, then a credential-bearing adapter verifies call/account/number ownership. Tenant-bound transactional receipts deduplicate delivery and preserve deletion tombstones. A callback cannot assign scopes, identify its notification recipient, or authorize a subsequent call.
- Make lifecycle limitations explicit. A possibly dispatched call is reconciled without redial. Cancellation after dispatch needs provider termination. Inbound spending begins at Bland before a callback reaches Veetbot; only verified provider controls can bound that admission. Inbound concurrency follows the reviewed provider plan's limits, independently of Veetbot's single concurrent outbound reservation. A stricter one-at-a-time inbound limit is optional, not an activation requirement.
- Introduce the workstream with its complete contract. Milestone 27 registers the canonical design and gates before implementation and does not advance the verified sequential ceiling. The correspondence storage and content-free notification trigger are explicit plan amendments. Existing acceptance criteria remain intact; project state records the new workstream.
Consequences¶
The owner receives a public receptionist and approved outbound calling without giving strangers a private assistant session. A private configuration ceremony, number selection, provider limits, fixture verification, and authorized live smoke remain necessary for activation. Local records need owner access control, retention, erasure, reconciliation, and distinct provider-retention reporting.
Acceptance updates the canonical plan, detailed specification, state, and registry together. This ADR is not implementation or live-release evidence. The proposal's defaults remain reviewable; it does not imply permission to buy a number or make a live call.
Alternatives considered¶
- Pair callers to the owner: rejected; caller ID and a public number do not authenticate a person or authorize use of the owner's account.
- Give Bland a general Veetbot API token: rejected; a voice-model prompt cannot enforce the deterministic policy boundary.
- Expose the entire provider MCP catalog: unnecessary for the requested runtime; administration belongs in operator setup.
- Only configure outbound calling: insufficient; the owner requested public incoming calls as well.
- Return a realtime Veetbot response to every utterance: deferred; it needs separate latency, interruption, authentication, and in-call approval contracts.