Click-to-call button

A call button for customer records. Click it: the agent's phone rings first, the customer is bridged in on answer, and the button narrates every step.

Click it — the demo simulates the full lifecycle: dialing → ringing → connected (with a live timer) → ended.

Installation

npx shadcn@latest add @handset/click-to-call-button

Usage

import { ClickToCallButton } from "@/components/handset/click-to-call-button";

<ClickToCallButton
  from={tenant.phoneNumberId}      // caller ID both parties see
  to={customer.phone}              // who you're calling
  connectTo={agent.directLine}     // the agent's phone rings first
  transcribe                       // optional: live speech-to-text
/>

This wraps useClickToCall: one POST to /calls, then status polling until the call settles. Outcomes (no answer, failed) show briefly, then the button returns to idle. Add onStatusChange to open a transcript panel the moment the call connects.

Security note: the proxy's POST /calls allowlist entry ships with a reminder to validate connect_to server-side against the signed-in agent's own number, so users can't ring arbitrary phones.

Props

Props
PropTypeDefaultDescription
fromstringTenant number id (num_…) or E.164 — the caller ID.
tostringThe customer's number, E.164.
connectTostringThe agent's number, E.164 — rings first.
transcribebooleanfalseStream live speech-to-text (billed per transcribed minute).
onStatusChange(call: Call) => voidFires on every lifecycle change.
childrenReactNode"Call"Button label.
classNamestringMerged onto the wrapper.