lead forms + connectors
define data-driven forms the bot can raise, and wire where captured leads go.
lead forms + connectors
a form is a data-driven card the bot can raise with [!FORM:<id>] (booking, quote,
demo). connectors decide where a captured lead goes. both live in your tenant config in
KV โ no code, no redeploy.
define a form
a form is a FormSpec: an id (what [!FORM:<id>] references), a title, an ordered list
of fields, and optionally connectorIds to scope which connectors receive its leads
(default = all configured).
field type is one of text | email | tel | textarea | select (select uses
options). the widget renders the form entirely with createElement/textContent (never
innerHTML), so field labels/options are XSS-safe.
teach the bot to raise it
the bot only offers a form if it knows the form exists. the server injects the list of your
forms into the system prompt automatically (<id> (<title>)), instructing the model to
append [!FORM:<id>] when a visitor is ready for a concrete next step it can't complete in
chat. you don't wire this by hand โ configuring a form is enough.
connectors โ where a lead goes
| type | fields | behavior |
|---|---|---|
telegram | โ (uses the top-level bot token) | the lead is posted into the visitor's topic |
email | toAddress | rendered + sent via Resend (silent no-op without RESEND_API_KEY) |
whatsapp | phone (E.164 digits, no +) | a wa.me CTA link in the widget; also a "Reply on WhatsApp" button in the lead email |
instagram | profileUrl | a profile-link CTA in the widget |
telegram and email are delivery channels โ the lead is sent for you. whatsapp and
instagram are CTA-only: the widget renders them as links for the visitor to tap;
nothing is delivered server-side for them, and they never leave the server as data.
the two capture paths
[!FORM:<id>]โ the widget renders yourFormSpec; on submit itPOSTs to/api/lead, which fans the values out to the scoped connectors.[!HANDOFF]with no form โ the widget shows a minimal name + contact capture; on submit itPOSTs to/api/contact(a back-compat shim that maps onto the same lead fan-out).
write the config
push it with the CLI (it merges โ unset fields are preserved):
set-kbase writes the systemPrompt; for forms/connectors/theme you POST the same
shape to /api/tenant/config (see reference โ edge routes).