Skip to main content
Internal documentation for developers. This page covers the Forms API: form creation, chat widget embedding, submission processing, and the form builder schema. Not intended for end users.

Route Map


Auth Pattern

Forms have dual access patterns: Authenticated (dashboard):
Public (embedded widget):
  • POST /api/forms/[formId]/chat can be called without authentication
  • Protected by formId secrecy (HMAC-signed if needed)

Form Schema

Forms are defined as a JSON schema stored in the config field:

Chat Submission Endpoint

The POST /api/forms/[formId]/chat endpoint processes a form submission through the AI:
This powers the form widget’s conversational experience.

Streaming Contract

forms/[formId]/chat/route.ts returns a text/event-stream response immediately, then delegates execution to the assigned agent through /api/agents/[id]/query. The route buffers endpoint_response chunks and validates the final payload with ChatResponse.safeParse before reading answer, messageId, or metadata. The route emits metadata SSE events only for typed form-state updates:
When a final messageId is present, the persisted message is updated with the marker-stripped answer and the typed form metadata. Parsing failures in tool or endpoint buffers are ignored deliberately; malformed buffers do not create partial database writes.

Submissions

Submissions are stored as JSON blobs — the exact structure depends on the form’s field schema.

Prisma Indexes Used


Known Issues / Gotchas

  • Public chat endpoint: The formId is the only secret protecting public form submissions. For sensitive forms, consider adding HMAC signing or reCAPTCHA.
  • Submission JSON schema evolution: If form fields change after submissions exist, old submissions may have fields that no longer match the current schema. UI must handle missing fields gracefully.
  • AI processing timeout: The chat submission endpoint calls the AI, which can take up to 30 seconds for complex forms. Configure appropriate Next.js function timeout.
  • Webhook delivery: External webhook on submit is fire-and-forget. Implement retry logic in @zappway/lib/webhooks if reliability is required.