Build a voice agent app
A small but complete application on Whissle — agents declared in one file, live voice calls with or without a talking face, and every past session with its transcript and score. It runs on a laptop in five minutes, and the code is public.
What you are building
One folder — a JSON file, a 200-line server and a single HTML page — that stands up four working voice agents and the screens around them. It ships inside the public SDK repository as examples/interview-platform.
Small is not the same as partial. It does everything a real integration does: creates and configures agents, ingests the knowledge that makes them know your domain, authenticates a user before minting them a session, carries a live conversation with a rendered avatar, and reads back transcripts and grades afterwards.
@whissle/agents0.4.0@whissle/sdk0.2.0@whissle/cli1.0.1wsk_ secret key carries full authority over your workspace. Never ship it to a browser.Get a key
- Sign in at whissle.ai.
- Settings → API Keys → Create.
- Grant
agents:read,agents:write,kb:read,kb:write,calls:read. - Copy the secret. It is shown once.
Connect the CLI
npm i -g @whissle/cli
whissle login # paste the wsk_ key when prompted
whissle whoami # confirms the workspace and your role/health answers 200 with and without it, so check an API route: /bot/api/whoami → 401, /api/whoami → 404.Run it
git clone https://github.com/WhissleAI/agents_js_sdk.git
cd agents_js_sdk/examples/interview-platform
npm install
export WHISSLE_API_KEY=wsk_live_…
npm startThe whole app in one file
Everything specific to this app is in agents.json.
{
"id": "line-cook",
"name": "Interview — Line Cook",
"type": "skills_exam",
"avatar": "F2-TL",
"knowledge": "Poultry to 74 °C, ground meat 71 °C, …",
"interview": { "level": "entry", "skills": […], "questions": […] }
}Definitions become agents
const existing = (await whissle.agents.list()).find((a) => a.name === def.name);
const agent = existing ?? await whissle.agents.create({ … });
await whissle.embed.enable(agent.id, { origins: ["http://localhost:4000"] });
await whissle.kb.addSnippet(agent.id, def.knowledge, `${def.name} — reference`);whissle.agentTypes() and compose onto default_prompt.A call — with a face, or without
// server — behind YOUR auth
const session = await whissle.embed.sessionToken(agentId, {
metadata: { user, agent: def.id },
});
return json(res, 200, session); // the WHOLE descriptortransport and ice_servers. Hand back only the token and the client has to guess at both.What happened afterwards
const calls = await whissle.calls.list({ limit: 50 });
const call = await whissle.calls.get(id);
const result = await whissle.calls.result(id);The agent drives your UI
agent.on("server-message", (m) => {
if (m.t === "question") showQuestion(m.index, m.text);
if (m.t === "complete") finish();
});
agent.send("wrap-up"); // and you can talk backMaking it yours
- Replace
agents.json. - Rewrite the prompt builders.
- Swap
userFrom()for real authentication. - Put the id map in your database.
When something goes wrong
It hangs on “connecting”: almost always ICE. Use the ice_servers the mint returns.
The avatar does not move: check the page is on @whissle/agents 0.3.1 or later.
402: out of credit. 403: the key lacks a scope, and scopes are fixed at creation.
Reference
| The example | agents_js_sdk/examples/interview-platform |
| Source | github.com/WhissleAI/agents_js_sdk · public |
| API base | https://aws-gateway-backend.whissle.ai/bot |