Create bot
Full customer onboarding (rows, secrets, tenant token, next steps): Onboard tab.
Creates the bot row. Then add knowledge (Knowledge tab) and Re-train. Bulk onboarding: node tools/sync-knowledge.mjs --seed seed/<bot_id>.json (template in seed/TEMPLATE.json).
New bots run on the customer's own OpenAI + Pinecone keys and stay keys missing until Ray sets OPENAI_API_KEY_<ID>, PINECONE_API_KEY_<ID>, PINECONE_HOST_<ID> (wrangler secret put). Tick "platform keys" only for our own bots.
Settings —
null to remove it. Unknown keys are rejected. onboarding_steps (the chat page's checklist) is an array of { id, title, body, owner, links, requires } and is replaced as a whole. locations (one bot, several sites) is [{ id, name }]; items may carry a location, steps a by_location override — re-train after adding it to a bot with indexed items (older vectors have no location and drop out of retrieval). lead_fields adds questions to the details form ([{ id, label, type: text|select, options, required }], answers reach the hand-off email and the CRM); feedback_email gets every thumbs-down and its follow-up comment. tour_booking (tours through Cal.com) is { locations: [{ id, name, calcom_event_type_id, tz }], collect, label } and needs the Worker secret CALCOM_API_KEY_<BOTID>. redirect_to (cutover) sends this bot's chat page and embeds to another bot id. language is "auto" (answer in the visitor's language) or a code like "es" (always that language; the chat page's own strings follow it — write greeting and fallback_message in that language). fallback_messages ({ "ko": "…", "es": "…" }, replaced as a whole) gives a fallback per language, picked by the language the visitor writes in (or the pinned language); other languages get fallback_message translated automatically (once per language, cached); set an entry here to correct a translation. paused (admin token only, or the Pause button above) stops the chat with "unavailable". trace_prompts: true stores each question's full system prompt for the Conversations tab's trace view. digest_email receives the Monday declined-question digest (else the summary address); knowledge_doc_id (the /d/<id>/edit id of the content Doc, shared with the platform service account as an editor) is where approved answers are appended; refuse_questions lists questions the assistant is meant to decline (the seed's refuse list; "Do not answer" adds to it). problem_report_email turns on the chat page's "Report a problem" button and is where the report goes (for Partake, the Linear team's email intake).
Members —
People who open this page by signing in with their email (Cloudflare Access), no token to paste. Each row is one email on this bot with a role: viewer reads, editor also adds knowledge, trains, reviews feedback and cancels tours, owner also changes settings and members. Access must allow the email too (Easy Cloud does that). Adding or removing needs owner.
Onboard a customer
Creates the stub rows, lists the secrets still missing, issues the tenant token once the bot is credential-ready, prints the next steps. Secrets, Vapi and Cal.com stay CLI steps (docs/runbooks/onboarding.md). Every step is re-read from the API on Load.
1. Rows
2. Secrets
secret put? Redeploy that Worker (docs/gotchas/voice.md).
3. Tenant token
A customer repo's CI needs owner (it pushes settings). Hand staff an editor or viewer token for the admin page instead of the owner one; paused, credentials and site_origins stay admin-token-only whatever the role. A role never changes: issue a new token and revoke the old.
4. Next steps
Customer
Ray
Add knowledge
Upload documents
PDF, DOCX, Markdown or text, parsed in this browser (nothing is stored but the text). Each document becomes one file item per section (headings, else every ~20k chars); re-uploading the same file name replaces its sections. Raw sections are a staging area — draft Q-line items from them before relying on the answers.
Google Doc source
Pulls knowledge from a Doc published to the web (File → Share → Publish to the web; paste the …/d/e/2PACX-…/pub link that dialog shows, not the editor URL, and untick "Restrict access to your organization" or the sync sees a sign-in page). Use the Title style for the Doc's own title; a Heading 1 becomes an item. Headings named after a location in the bot's locations setting scope what follows to that location; "Shared" scopes back; every other heading is one item. Re-syncing updates items in place and removes sections deleted from the Doc, then re-trains. The link is remembered in knowledge_doc_url when an owner syncs (an editor's sync leaves the saved link as it was).
No Doc yet? New content Doc creates one from the template (an intro, five topics with the questions visitors ask and a bracketed note where the answer goes), publishes it to the web, shares it with the editors below (your own address is always included), saves both Doc settings on the bot and syncs once. From then on the hourly watch picks up every change. Owner or admin.
Crawl site
Lists the pages of the customer's own site (sitemap, else links) and saves each as a url item; re-crawling a page updates its row. The origin must be in the bot's site_origins setting (Bots tab); set recrawl_days there and the crawled pages refresh on their own every N days (changed pages re-train and start a test run). Raw pages are a staging area — draft Q-line items from them before relying on the answers.
Declined questions awaiting answers
What visitors asked that the assistant declined, deduplicated, with a drafted answer where the knowledge holds the fact. Approve publishes it (into the Google Doc when knowledge_doc_id is set, else straight into knowledge) and reruns the tests; Do not answer adds it to refuse_questions. The same list goes out by email every Monday to digest_email (else the summary address).
Proposed test questions
Real questions visitors asked and the assistant answered, drawn once a month (and by the button) as candidate test cases, each expecting the passage it was answered from. Accept adds it to the test set; reject retires it. Thumbs-downs we triaged as a miss on our side land here too.
onboarding_steps, the step they are on, and their last question.
tour_booking bots): upcoming ones by default. Cancel here cancels in Cal.com too and tells the CRM.
Set bot_id (top right) to see one bot in depth.
crm_outbox, all bots and businesses; admin token only). A failed row is retried every 5 minutes with backoff, up to 8 tries; Retry sends it now with the same id.
Usage
Per-tenant counters from usage_daily (chatbot: asks, trained items; voice: calls, seconds, bookings, messages). Tenant = the bot_id above; leave it blank for all tenants (admin token only).