Quickstart
Two ways in.
Both end in a call.
The fast path is your coding agent: connect it to the TalkWith MCP and ask for video. The manual path is three small files. Either way you'll need an account — sign up atthe dashboard first.
Path A · recommended
Point your agent.
Mint an account token in the dashboard atapp.talkwith.online, then add the TalkWith MCP to your agent's config.
Claude Code
claude mcp add talkwith \
--env TALKWITH_API_URL=https://api.eu.talkwith.online \
--env TALKWITH_ACCOUNT_TOKEN=vt_at_your_token \
-- npx -y @talkwith/mcpCursor — .cursor/mcp.json
{
"mcpServers": {
"talkwith": {
"command": "npx",
"args": ["-y", "@talkwith/mcp"],
"env": {
"TALKWITH_API_URL": "https://api.eu.talkwith.online",
"TALKWITH_ACCOUNT_TOKEN": "vt_at_your_token"
}
}
}
}Then, in your project, one prompt:
> add video calling to this appThe agent provisions a project, wires the keys into your environment, and scaffolds a<VideoRoom /> page plus a server-only token route — inside spend caps you set. Open two tabs on the scaffolded page and you're on a call.
Path B · manual
Wire it by hand.
Create a project in the dashboard to get a publishable key and asecret key, then it's an install, a token route, and a component.
- 01
Install the embed
npm install @talkwith/embed - 02
Add a server token route
Next.js App Router shown — app/api/video-token/route.ts. The secret key stays on the server;
createTokenrefuses to run in a browser.import { createToken } from "@talkwith/embed/server"; export async function GET(req: Request) { // TODO: verify the caller's session here — as written, anyone who can // reach this route can mint a token and join the room. const secretKey = process.env.TALKWITH_SECRET_KEY; if (!secretKey) { return Response.json({ error: "video_unconfigured" }, { status: 503 }); } const url = new URL(req.url); const { token, serverUrl } = await createToken({ endpoint: "https://token.eu.talkwith.online/token", publishableKey: "pk_...", // your project's publishable key secretKey, room: url.searchParams.get("room") ?? "demo", identity: url.searchParams.get("identity") ?? "guest", template: "group", // "one_to_one" | "group" | "recording" }); return Response.json({ token, serverUrl }); } - 03
Render the room
app/call/page.tsx —
<VideoRoom />ships a complete call UI; pass children to go fully headless with theuseVideoRoom/useParticipantMediahooks instead."use client"; import { VideoRoom } from "@talkwith/embed"; import { useEffect, useState } from "react"; export default function CallPage() { const [creds, setCreds] = useState<{ token: string; serverUrl: string } | null>(null); useEffect(() => { const identity = "guest-" + Math.random().toString(36).slice(2, 8); fetch("/api/video-token?room=demo&identity=" + encodeURIComponent(identity)) .then((r) => r.json()) .then(setCreds); }, []); if (!creds) return <p>Joining…</p>; return <VideoRoom serverUrl={creds.serverUrl} token={creds.token} />; }
before you shipGate the token route behind your own auth — derive identity androom from the signed-in session instead of trusting query params.