Skip to Content

Overview

Forms are defined in the character’s appearance configuration under the forms array. When a form is defined, the agent automatically gains a callable tool with the same name. During a call, the agent can:

  1. Open the form panel (optionally pre-filling fields with values it already knows)
  2. Track field changes in real time via the data channel
  3. Guide the user through multi-step flows verbally
  4. Submit the completed form to your endpoint (or to Oshara’s managed storage)

Minimal single-step form

{ "forms": [ { "id": "contact", "title": "Get in touch", "submit_url": "https://api.yoursite.com/contact", "fields": [ { "name": "name", "label": "Full name", "type": "text", "required": true }, { "name": "email", "label": "Email", "type": "email", "required": true }, { "name": "message", "label": "Message", "type": "textarea" } ] } ] }

Multi-step stepper example

{ "forms": [ { "id": "book-demo", "title": "Book a demo", "subtitle": "Tell us about your use case", "submit_url": null, "submit_label": "Confirm booking", "success_message": "We'll be in touch within one business day!", "steps": [ { "id": "contact", "title": "Your details", "fields": [ { "name": "first_name", "label": "First name", "type": "text", "required": true, "width": "half" }, { "name": "last_name", "label": "Last name", "type": "text", "required": true, "width": "half" }, { "name": "email", "label": "Work email", "type": "email","required": true }, { "name": "company", "label": "Company", "type": "text" } ] }, { "id": "use-case", "title": "Your use case", "fields": [ { "name": "use_case", "label": "What are you trying to build?", "type": "select", "required": true, "options": ["Customer support bot", "Sales assistant", "Internal helpdesk", "Other"] }, { "name": "details", "label": "Tell us more", "type": "textarea", "rows": 4 } ] }, { "id": "schedule", "title": "Preferred time", "fields": [ { "name": "date", "label": "Preferred date", "type": "date", "required": true }, { "name": "timezone", "label": "Your timezone", "type": "text" } ] } ] } ] }

FormDefinition schema

FieldTypeRequiredDescription
idstringStable identifier. Used as the tool name and for LiveKit topic matching.
titlestringHeading shown at the top of the form panel.
subtitlestringSubheading / context text below the title.
fieldsFormFieldDef[]one of fields or stepsFields for a single-page form. Ignored if steps is set.
stepsStep[]one of fields or stepsMulti-step stepper. Each step has its own fields array.
submit_urlstring | nullPOST target. Absolute URL or path relative to apiUrl. null = store in Oshara managed storage.
submit_method"POST" | "PUT" | "PATCH"HTTP method for submission (default "POST").
submit_labelstringSubmit button text (default "Submit").
success_messagestringMessage shown in the panel after successful submission.
disabledbooleantrue = form is not exposed to the agent as a tool. Useful to temporarily hide a form.
topicsstring[]Extra LiveKit data-channel topics that trigger this form to open.
event_typesstring[]Legacy event_type aliases for backwards compatibility.
confirmation_topicstringTopic on which the widget echoes a confirmation message after submission (default "voice.user_text").
confirmation_typestringtype field on the confirmation payload (default "{id}_submitted").
layout.field_layout"stack" | "grid""grid" enables two-column layout (use width: "half" on fields).
layout.density"comfortable" | "compact"Spacing density.
layout.label_position"top" | "inline"Label placement.

Step object

FieldTypeDescription
idstringStep identifier.
titlestringStep heading (shown in the stepper header).
subtitlestringStep subheading.
fieldsFormFieldDef[]Fields for this step.
next_labelstring”Next” button label override.
back_labelstring”Back” button label override.

FormFieldDef schema

FieldTypeDescription
namestringField key — used as the key in the submitted JSON object. Not required for type: "display".
labelstringLabel text shown above the field.
typesee belowInput type.
placeholderstringPlaceholder text.
requiredbooleanMarks the field as mandatory (client-side validation).
optionsstring[] | {value, label?}[]Choices for select and radio fields.
rowsnumberNumber of visible rows for textarea.
default_valuestringPre-filled value. The agent can also override this when opening the form.
help_textstringSub-label hint shown below the field.
patternstringRegex pattern for client-side validation.
minnumberMinimum value (for number) or minimum character length (for text fields).
maxnumberMaximum value / length.
width"full" | "half"Column width when field_layout is "grid".

Field types

TypeRenders as
textSingle-line text input
emailEmail input (validated format)
telPhone number input
textareaMulti-line text area
selectDropdown <select>
numberNumeric input
dateDate picker
timeTime picker
checkboxSingle checkbox (value is "true" / "false")
radioRadio button group
displayRead-only text block (no name needed)

Submit behaviour

submit_url: null — managed storage

When submit_url is null, the widget POSTs the form values to:

POST /api/agents/{slug}/form-responses/

You can retrieve submissions via the Form Responses API.

submit_url: "https://..." — direct POST

The widget sends a JSON POST directly to your URL with the body:

{ "field1": "value1", "field2": "value2" }

The endpoint should return any 2xx status to indicate success.

Confirmation echo

After a successful submission the widget publishes a confirmation message on the LiveKit data channel so the agent knows the form was completed:

{ "type": "book-demo_submitted", "form_id": "book-demo", "text": "I have confirmed the booking form submission.", "form": { "first_name": "Alice", "email": "alice@example.com" } }

The agent uses this to continue the conversation naturally (“Great, I’ve got your details — someone will reach out within 24 hours!”).

How the agent opens a form

The agent calls a tool named after your form’s id. For example, with id: "book-demo", the tool invocation looks like:

{ "name": "book_demo", "arguments": { "first_name": "Alice", "email": "alice@example.com" } }

Any argument matching a field name is used as a pre-fill value. The widget opens the form panel and populates those fields immediately.

Last updated on