- A FULL Mode avatar session running in the browser, on your own domain.
- Your own OpenAI-compatible endpoint driving every turn of the conversation, with a prompt you control.
- A summary of each conversation landing in Notion and Slack the moment the visitor disconnects.
liveavatar-sales-agent
Reference implementation for this guide. Deploys to Vercel; MIT licensed.
Prerequisites
You do not need your own avatar. The repo ships the public demo avatar id, which any account can use. Notion and Slack are optional.
Quickstart
npm run setup picked an avatar, generated the signing secret, and wrote it all to .env.local. It also spins up a basic context that supplies the greeting and can help with the remaining pieces of this integration. It inspects your current configuration and is safe to rerun. Every variable it writes is documented in the repo’s .env.example.
What you’re talking to right now is your account’s default LLM, not this app’s sales prompt. Getting that swapped in is the rest of the guide — and it needs a URL LiveAvatar can reach, which localhost isn’t. So: deploy, then wire up the brain.
npm run setup warns that ANTHROPIC_API_KEY is missing. Ignore it for now — nothing you’ve run so far needs it. It comes in when you connect the brain.Connecting your personal brain
So far all we’ve done is spin up a quick LiveAvatar setup. Next we connect it to your own LLM, so the agent answers from your prompt and your data instead of the account default. For this demo we provide a basic custom LLM setup that we’ll deploy to Vercel. It’s the/api/chat/completions route in the repo — an OpenAI-compatible endpoint wrapping a basic Anthropic model, which assembles the sales prompt on every turn before it answers. Swap the model or the prompt later; for now, deploy it as-is by following the steps below.
1
Add your Anthropic API key
The endpoint needs a model to call. Grab a key from console.anthropic.com and add it to The same key writes the post-call summary later.
.env.local:2
Deploy
Deploy before provisioning. LiveAvatar calls your endpoint over the public internet, so the LLM configuration has to point at a URL that already exists — which means the deployment comes first. The build reads no configuration, so deploying with nothing set works fine.
3
Provision against the deployment
.env.local. See what it creates if you’d rather do it by hand.Use the production domain, not a preview URL. Preview URLs change per commit, so a configuration pointed at one breaks silently on your next push.4
Copy the env vars into Vercel
.env.local is gitignored and never deploys. Project → Settings → Environment Variables accepts a pasted .env file, so paste the contents of .env.local — including the Anthropic key — and review before saving. Skip VERCEL_OIDC_TOKEN if present; Vercel injects that itself.5
Redeploy
npm run dev) so it picks up the new .env.local, then start a session — or use the deployed URL directly, either works.
Your LiveAvatar session now calls your deployed endpoint on every turn instead of the account default LLM, so the agent is answering from your prompt. This holds even when you mint from localhost: LiveAvatar always calls the public base_url, so the brain requests show up in your deployment’s logs, never your terminal.
See The prompt for how that prompt is put together and where to edit it.
The prompt
The system prompt is assembled from the Markdown files insrc/lib/ai-sales/brain/prompt-parts/, concatenated in filename order:
- Every
*.mdin the directory is a part — adding or removing one needs no code change. - Files prefixed with
_are ignored, so a part can be parked without deleting it. - YAML frontmatter and HTML comments are stripped, so
<!-- … -->notes are documentation for you, not input for the model. {{AGENT_NAME}},{{AGENT_ROLE}},{{PRODUCT_NAME}}and{{COMPANY_NAME}}are substituted from env vars.
OPERATOR SCAFFOLD comment marking where your pricing, competitor handling, and customer stories go. Set PROMPT_PARTS_DIR to replace the bundled prompt wholesale at deploy time — useful for keeping a real sales prompt outside the repo.
What the setup creates
Two objects on the LiveAvatar side — a secret holding the key that protects your endpoint, and an LLM configuration pointing at it:secret_type is OPENAI_API_KEY because the endpoint speaks the OpenAI chat/completions protocol. The secret_value goes in AI_SALES_LLM_CONFIG_API_KEY and the returned configuration id in LLM_CONFIGURATION_ID. At runtime LiveAvatar presents the key back as Authorization: Bearer <value> and the app compares it in constant time.
Leave LLM_CONFIGURATION_ID unset and sessions fall back to your account’s default LLM. See Custom LLM for the full endpoint contract.
After the call
While we don’t do it in this walkthrough, you can set up configurations to run once the session ends. For example, we configure our calls to add entries in our Notion CRM database and fire an alert to our Slack channels.npm run setup doesn’t provision either — they’re your own workspaces.
Notion. Create an internal integration at notion.so/my-integrations, share your database with it, then set NOTION_TOKEN and NOTION_DATABASE_ID. The app upserts by email — one page per lead, each conversation appended — so the database needs matching properties. Duplicate the published template instead of building the schema by hand:
LiveAvatar Sales Agent — Notion template
The CRM schema this app writes to.
SLACK_WEBHOOK_URL. The webhook is bound to one channel at creation time, so the destination is chosen there, not in the app. Notion runs first so its page link can be included in the Slack message.
Productionization next steps
What ships here is a demo — enough to have a real conversation and see a lead land in a CRM. A few directions to take it from there: Move off the Vercel deployment. The brain is a serverless route because that’s the fastest thing to stand up. A dedicated backend gives you persistent connections, no cold starts on the first turn of a conversation, and somewhere to put the rest of your sales stack. The contract with LiveAvatar doesn’t change — it’s still an OpenAI-compatible endpoint behind abase_url.
Inject your own data into the prompt. The demo assembles the prompt from static Markdown plus whatever the visitor types. The interesting version pulls from what you already know about them — your CRM, a Typeform they filled out, product usage, the page they came in from — and drops it into the prompt before the first turn.
Fire workflow automations on session end. Notion and Slack are two examples of the same idea: the session-end route hands you a structured summary of the conversation. Send the visitor a follow-up email, open a deal in your CRM, page an account exec if the lead qualified, kick off a nurture sequence.
Appendix: How a session flows
- Mint.
/api/ai-sales/sessioncallsPOST /v1/sessions/tokenwith yourLIVEAVATAR_API_KEY, in FULL Mode, passing the avatar, the context, and the LLM configuration id. At the concurrency ceiling or out of credits it returns503 { error: { code: "busy" } }and the UI shows a retry state. - Connect. The browser joins the room with the Web SDK. The avatar immediately speaks the context’s
opening_text— before any model is in the loop. - Converse. LiveAvatar calls your
/api/chat/completionsover the public internet on every turn, authenticated with a bearer key you registered as a secret. The route assembles the persona, real-world context (date, weather), an optional lead profile, and any prior CRM history for that email, then streams the model’s reply back as OpenAI-shaped SSE. - Close. On disconnect the browser posts to
/api/ai-sales/session-endwith an HMAC signature; the route summarizes, upserts the Notion page, then posts to Slack with the page link.