Quick start
Get a chatbot live on a website in five minutes.
-
1
Deploy & sign in
Follow the deploy guide to spin up Buildx on your own infra. Sign in with the
ADMIN_EMAIL/ADMIN_PASSWORDyou configured. -
2
Configure API keys
In Settings, add your Gemini API key and Qdrant connection (URL + API key + collection name). Hit Test to verify both.
-
3
Create a bot
From My Bots → Create Bot, give it a name and an initial system prompt. Pick a Gemini model (Flash is a sensible default).
-
4
Upload knowledge
Switch to the Knowledge tab, drop a PDF / DOCX / TXT. Indexing is automatic — chunks land in Qdrant within seconds.
-
5
Embed
Copy the embed snippet from the bot editor and paste it before
</body>on your site. Done.
Settings
Required keys and connection settings — environment variables + in-app fields.
Buildx stores credentials encrypted (AES-256-GCM) at the application level. Two integration points are required: a Gemini API key for the LLM & embeddings, and a Qdrant collection for vector search.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
| ADMIN_EMAIL | Yes | Dashboard login email. |
| ADMIN_PASSWORD | Yes | Dashboard login password. |
| AUTH_SECRET | Yes | Session JWT signing secret. |
| MONGODB_URI | Yes |
MongoDB connection string. The DB name is forced to
buildx.
|
| ENCRYPTION_KEY | Recommended | 64-char hex string. If absent, stored API keys are kept in plaintext (warning logged). |
Set ENCRYPTION_KEY before the first time you save a
Gemini or Qdrant API key — keys saved without it will be stored
in plaintext until re-saved.
In-app settings
- Gemini API key — used for chat completion AND the 768-dim embedding model.
-
Qdrant URL — host URL of your Qdrant instance,
e.g.
https://your-cluster.cloud.qdrant.io. - Qdrant API key — encrypted at rest.
- Qdrant collection name — created on first save (idempotent), 768-dim cosine.
-
The System Health widget polls
/api/healthevery 30s — green dots mean Mongo is reachable and a Gemini key is on file.
Bots
Each bot is a separately-themed conversation surface with its own prompt, model, and knowledge base.
System prompt
The single most important setting. Be specific about role, scope, and tone.
Instead of "You are a helpful assistant", write:
You are a Customer Support Agent for Acme Corp.
You ONLY answer questions about Acme products.
If asked anything else, politely redirect to support@acme.com.
Be concise (max 3 sentences) and professional.
Models
Flash
gemini-2.5-flash
Default. Best speed-to-quality. Use for support chat & FAQ.
Pro
gemini-2.5-pro
Highest quality. Slower + more expensive. For complex tasks.
Flash Lite
gemini-2.5-flash-lite
Fastest. Cheapest. Light-weight Q&A only.
Temperature
0.0–0.3 for factual support.
0.4–0.7 for general chat.
0.8–1.0 for creative writing. Default is 0.7.
IDs & status
- Public ID — 12-char nanoid, used in the embed snippet and chat API. Never changes.
- Internal ID — Mongo ObjectId. Used by the dashboard for editing.
- Active toggle — soft-disables the bot. Disabled bots return 403 from the chat API.
Knowledge base (RAG)
Documents you upload are chunked, embedded, and retrieved at query time.
How it works
-
Extract — text is pulled from the file (PDF via
unpdf, DOCX viamammoth, TXT direct). - Chunk — split greedily on newlines / spaces into overlapping windows (1000 chars, 200 overlap).
- Embed — each chunk becomes a 768-dim vector via Gemini's embedding API.
-
Index — vectors are upserted into Qdrant with
botId+sourceIdpayload filters. - Retrieve — at chat time, the user's message is embedded and the top-3 chunks (cosine similarity) are appended to the system prompt.
Supported formats
.pdf · .docx · .txt · max
10 MB per file.
- Prefer well-structured documents (clear headings, short paragraphs).
- One concept per chunk works best — avoid 50-page wall-of-text PDFs without sectioning.
- The chat handler swallows RAG errors silently — if your bot stops citing your docs, check Qdrant connectivity in Settings.
Deleting a document drops its vectors from Qdrant + the KnowledgeBase row. Deleting a bot wipes everything: Qdrant points, KnowledgeBase rows, KnowledgeChunk rows, and any captured leads.
Design studio
A full-screen editor with live preview for theming the chat widget.
Open from the bot editor sidebar via
Embed & Distribution → Design Studio →. Every
change updates the iframe preview instantly via
postMessage — saved to the database only when you
press Save.
Sections
- Global Settings — font family, corner radius (cascades to bubbles, input, card), shadow.
- Header — title, subtitle text + font, icon (custom SVG, color, bg, size), refresh icon color.
- Chat Window — page background, empty-state icon (custom SVG + colors), widget size (drives the iframe container).
- Bot & User Message — bg, text, font (size/weight/family), border radius (preset + slider + per-corner mode), avatar.
- Input Area & Footer — input background, text, border, placeholder text + color, send button.
- Launcher — color, icon SVG, shape, border, size sliders, close-state colors.
- Advanced — element visibility toggles, text overrides.
Lead capture
Bots can save user contact details into a leads inbox using Gemini function calling.
Enable on a bot via the editor's Tools tab. When
the bot detects intent (pricing, demo, contact request), it asks
for the required fields conversationally, then saves a
Lead via the
capture_lead tool. Captured leads appear in the
dashboard's Leads inbox.
Configuration
- Required fields — chips for name, email, phone. The bot won't save until it has these.
- Qualification prompt — appended to the system prompt. Useful for BANT-style questions.
- Duplicate window (hours) — same email + same bot inside this window updates the existing lead instead of creating a duplicate. Default 24h. Set to 0 to disable.
Three-layer dedup
-
Model — system prompt instructs the bot to call
capture_leadat most once per conversation. - Conversation — every chat session has a UUID; subsequent calls in the same UUID merge into one lead row (DB-level unique partial index).
- Identity — within the dedup window, captures matching the same email update the existing row.
Lead capture rides on the existing
POST /api/chat handler. Bots without the toggle on
are unaffected.
Embedding
A single <script> tag drops the chat widget anywhere — no build step required.
Copy the snippet from the bot editor (Public ID copy button) and
paste it before the closing </body> tag of your
site:
<script
src="https://your-buildx-domain.com/embed.js"
data-bot-id="PUBLIC_ID_HERE">
</script>
What gets injected
-
A floating launcher button at
bottom-rightby default (positionable per bot). -
A hidden iframe-container that loads
/share/[publicId]?embed=truewhen the launcher is clicked. -
All sizing, colors, icons, and shadows are driven by the saved
theme — no
data-*attributes to set.
Allowed domains
By default a bot is embeddable anywhere. To restrict it, add hostnames to Security → Allowed Domains. The check runs server-side both on the public config endpoint and on every chat request.
Embed against http://localhost:3000/embed.js while
developing. The /share/* route sets
frame-ancestors * in CSP so it works inside an
iframe on any host.
Mobile
Below 480px viewport width, the iframe expands to fill the screen automatically. The launcher remains in its configured corner.
API reference
Public, CORS-enabled endpoints used by the embed widget — and available to your own integrations.
POST /api/chat
Stateless. Rate-limited (default 20 req / 60s per IP). CORS *.
curl -X POST https://your-buildx-domain.com/api/chat \
-H "Content-Type: application/json" \
-d '{
"botId": "PUBLIC_ID",
"message": "Hello",
"history": [],
"conversationId": "<uuid-or-omit>"
}'
# Response
{ "message": "...markdown text...", "model": "gemini-2.5-flash", "leadCaptured": false }
GET /api/bots/public/[publicId]
Returns the renderable bot config — no auth required. CORS *.
{ "config": {
"publicId": "...",
"name": "...",
"theme": { ... },
"widgetPosition": "bottom-right",
"allowedDomains": [],
"isActive": true
}}
GET /api/health
Liveness + service connectivity. No auth.
{ "status":"healthy", "uptime":123.4, "timestamp":"...",
"services": {
"database": { "status":"up", "latency":12 },
"gemini": { "status":"configured" }
}
}
/api/chat uses substring match against
Origin/Referer. /api/bots/public/[publicId] uses
stricter hostname match. If you whitelist
example.com, requests from
example.com.evil.tld may pass
/api/chat — keep the list tight.
Troubleshooting
The most common things that go wrong, and how to spot them.
Bot returns generic answers and ignores my uploaded docs
Check Settings → Qdrant connection ("Test" button). The chat handler logs RAG errors to the server console but continues without context — so the bot keeps replying, just without your knowledge. Verify the collection name matches and the API key isn't stale.
Embed snippet doesn't show a launcher
Open the host site's devtools console. Look for
Bot CMS: errors. Common causes: (1) the public ID
is wrong, (2) the bot is marked inactive, (3) Allowed Domains
doesn't include this host.
Lead capture isn't firing
Verify the Tools tab toggle is on AND the system prompt
fragment is appended (open the bot in design studio, the live
preview shows it). Required fields must all be present. The
model only calls capture_lead after detecting
clear intent — try saying "I want a demo, my email is
x@y.com".
Theme changes save but don't appear on the live widget
Browser caching. Hard-refresh the host page (the
embed.js is served with no cache-busting by
default). The iframe content updates without caching since it
loads /share/[publicId] fresh each open.
"Rate limit exceeded" on chat
Default is 20 requests / 60s per IP. Behind a proxy, ensure
x-forwarded-for is set correctly — without it,
all traffic shares one bucket.
API keys stored in plaintext warning in logs
Set ENCRYPTION_KEY in your environment (64-char
hex string). After setting, save the key in Settings again —
it'll be encrypted on write.
Open an issue at github.com/MayonLabs/buildx/issues — we read every report.