The four moving parts
- Define the form on the character (via
PATCH /api/ai-characters/{slug}/) - Render it — either via the widget, or in your own UI by reading
GET /api/agents/{slug}/appearance/ - Submit — widget/UI POSTs values to your
submit_url, or to Oshara’s managed storage ifsubmit_url: null - Retrieve — for managed storage, list submissions via
GET /api/agents/{slug}/form-responses/
Define forms via API
Forms live insidewidget_appearance.forms on the character:
id (hyphens become underscores: book-demo → tool book_demo).
Multi-step (stepper) forms
Usesteps instead of fields:
Receive submissions at your endpoint
Setsubmit_url to an absolute URL on your backend. The widget (or your custom UI) POSTs a flat JSON object — field name → value:
| Behaviour | Status code |
|---|---|
Success → widget shows success_message, agent continues | any 2xx |
| Failure → widget shows error, agent notified to retry | any non-2xx |
submit_method
Defaults to POST. Override on the form definition:
Submit to Oshara managed storage
Setsubmit_url: null and the widget POSTs to:
session_id is what POST /api/agents/agent-session/ returned at session start.
Retrieve submissions
| Param | Effect |
|---|---|
form_id=book-demo | Only this form |
session_id=sess_abc123 | Only this call |
Rendering forms in your own UI
Fetch the schema via:appearance.forms[*].fields (or appearance.forms[*].steps[*].fields for steppers) and render each one with your component library. Validate before submit using required, pattern, min/max on the field schema.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
| Submission not received | submit_url returns non-2xx or wrong URL | Log requests; confirm absolute URL |
403 on appearance/ GET | Origin not whitelisted | Add domain to allowed_origins |
| Agent never opens form | Form id not in system prompt instructions | Add “when X happens, open form Y” to prompt |
disabled: true on form | Form hidden from agent on purpose | Set disabled: false to expose |
