Knowing the caller
A call often starts with something already known: the number that dialed in, the page the caller came from, the email of a logged-in visitor. Use it so the agent does not have to ask.
| Hook | What it reads | Can you trust it |
|---|---|---|
useAgentInfo() | What the call knows: caller.phoneNumber, call.direction and more (the full list) | Yes, the framework or your host set it |
useClient() | What the caller’s page said, the client.* keys | No, anyone can edit a link |
client.* is written by the page
useClient() reads these keys as one object without the prefix,
const { email, event } = useClient(); a key the page did not send is undefined.
Whoever can edit a link or a page can set client.* to anything, so:
- Use it to prefill and to skip a question the page already answered (a logged-in
visitor’s email), never for access, identity or prices. No locked branch, skill, tool
or account data depends on a
client.*value; to know who the caller is, ask, or check in a tool against your own system. - Treat it as untrusted text. Look a value up in a table you own rather than rendering it, so an unknown value renders nothing. When you do render one, quote it, keep it short, and render only the keys the agent needs.
- It may be absent: a visitor from another page, a link without its parameters, a context the host refused. The agent must work without it.
import { Text, useClient } from "curb";
// Your own words for the events your links name. Any other value renders nothing.
const EVENTS: Record<string, string> = {
"webinar-11-04": "the webinar on 4 November",
"webinar-11-17": "the webinar on 17 November",
};
export function EventContext() {
const { event } = useClient();
const words = EVENTS[event ?? ""];
return words ? <Text>The caller came from the page of {words}. Mention it in the greeting.</Text> : null;
}
With firstTurn: "agent" the agent greets once the caller is there. With
firstTurn: { say } it opens with exactly that line instead, with no model call, written
from the info above when say is a function (Agents and skills, “Who speaks
first”). Neither opening can be interrupted: the caller hears the whole opening line, speech
during it is not taken as a turn, and a message typed during it is answered once it has
played. A call attached to a conversation that already has turns (typing in the preview
before starting the call) does not open it with the line again.
Tell the agent which event a call comes from
Attendees of a webinar click a call link, and the agent should know which webinar they come from without asking. Name the event in the link, let the widget pass it on, and let the agent look it up.
-
Make the call page from the call link recipe and add
data-context-params="event"to its widget tag. The widget then copies the page address’seventparameter into the call’s context (see Tell the agent where a call comes from):<script src="https://cdn.kugelaudio.com/widget/v1/widget.js" data-agent="YOUR_WIDGET_ID" data-position="inline" data-size="lg" data-label="Start the call" data-context-params="event" async></script> -
Send each event’s attendees their own link, such as
https://example.com/call?event=webinar-11-04for one webinar andhttps://example.com/call?event=webinar-11-17for the next. Keep email addresses, names and anything else personal out of the link: whoever has the link can read it, and the context is stored with the call. To tell attendees apart, add a token that means something only to your own systems, such as&t=8f3k2q, and name it too:data-context-params="event,t". -
In the agent, map the event ids of your links to your own words, and render the sentence for the one the call came from.
useClient()reads the call’s context; itseventis the link’sevent:import { Text, useClient } from "curb"; // Your own words for the events your links name. Any other value renders nothing. const WEBINARS: Record<string, string> = { "webinar-11-04": "our webinar on getting started", "webinar-11-17": "our webinar on advanced setups", }; export function WebinarContext() { const { event } = useClient(); const webinar = WEBINARS[event ?? ""]; return webinar ? ( <Text>The caller comes from {webinar}. Mention it in your greeting and answer questions about it.</Text> ) : null; }Render
<WebinarContext />inside<Agent>insrc/agent/agent.tsx, after<SystemPrompt>.
The agent knows the event from its first sentence. Anyone can edit a link, so the event is a hint and never a reason to unlock anything: a value your table does not know renders nothing, and the agent talks as it would without one.
Pass what you already know (a logged-in user)
A visitor who is logged in on your website should not have to spell their email address for the agent. Give the widget what your page already knows, and let the agent use it instead of asking.
-
On the page, set the widget’s
contextproperty once you know who is logged in, and set it tonullwhen they log out:customElements.whenDefined('kugelaudio-agent').then(() => { document.querySelector('kugelaudio-agent').context = { email: user.email, name: user.name } })The next call to start carries it (see Tell the agent where a call comes from). The property stays in the page’s memory and is never written into its HTML, so a session recording, which captures the HTML, does not capture the address. For the same reason, keep personal details out of the tag’s
data-context, even on a page rendered on the server. -
In the agent,
useClient()reads the call’s context, each key without theclient.prefix. Tell the model the address so it does not ask:import { Instruction, useClient } from "curb"; export function KnownCaller() { const { email } = useClient(); // Text from the page: only something shaped like an address gets in. if (!email || !/^[^\s@"]+@[^\s@"]+$/.test(email)) return null; return <Instruction>{`The caller is logged in with the email address "${email}". Use it and do not ask for it.`}</Instruction>; }Render
<KnownCaller />inside<Agent>insrc/agent/agent.tsx, after<SystemPrompt>. Without an address it renders nothing, and the agent works as before. -
Try it locally.
--clientgives the conversation the context a page would send:npx curb say --new --client email=max@example.com "Hi"Continue with a request that needs the address: the agent uses max@example.com instead of asking for it.
The page sets the context, and anyone who controls the page can change it. Use it to prefill and to skip questions, never as proof of who the caller is or to unlock their account’s data; to act on an account, check the caller in a tool against your own system. Send only what the agent needs: the context is stored with the call.
Next steps
- Calls from your website: how a page passes this context.
- Who speaks first: use it in the greeting.