> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zappway.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Forms API

> Internal reference for the Forms API — form CRUD, chat/widget submission handling, response management, and form builder integration.

> **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

| Method   | Endpoint                          | Handler File                          | Description                   |
| -------- | --------------------------------- | ------------------------------------- | ----------------------------- |
| `GET`    | `/api/forms`                      | `forms/route.ts`                      | List all forms                |
| `POST`   | `/api/forms`                      | `forms/route.ts`                      | Create a form                 |
| `GET`    | `/api/forms/[formId]`             | `forms/[formId]/route.ts`             | Get form by ID                |
| `PATCH`  | `/api/forms/[formId]`             | `forms/[formId]/route.ts`             | Update form                   |
| `DELETE` | `/api/forms/[formId]`             | `forms/[formId]/route.ts`             | Delete form                   |
| `POST`   | `/api/forms/[formId]/chat`        | `forms/[formId]/chat/route.ts`        | Submit form response via chat |
| `GET`    | `/api/forms/[formId]/submissions` | `forms/[formId]/submissions/route.ts` | List form submissions         |

***

## Auth Pattern

Forms have **dual access patterns**:

**Authenticated (dashboard):**

```ts theme={null}
withPermissionRoute(req, { permission: 'forms.manage' }, handler)
```

**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:

```ts theme={null}
interface FormConfig {
  fields: FormField[];
  submitButtonText: string;
  successMessage: string;
  agentId?: string;      // AI to process submission
  webhookUrl?: string;   // External webhook on submit
}

interface FormField {
  id: string;
  type: 'text' | 'email' | 'phone' | 'select' | 'multiselect' | 'textarea' | 'number';
  label: string;
  placeholder?: string;
  required: boolean;
  options?: string[];    // for select/multiselect
  validation?: {
    pattern?: string;    // regex
    min?: number;
    max?: number;
  };
}
```

***

## Chat Submission Endpoint

The `POST /api/forms/[formId]/chat` endpoint processes a form submission through the AI:

```ts theme={null}
// Input: form field responses
// Processing:
// 1. Validate all required fields
// 2. Create Contact from submission data (if email/phone provided)
// 3. Create Conversation linked to the form
// 4. Pass responses to assigned AI Employee
// 5. AI generates a personalized response
// 6. Return AI response + conversationId
```

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:

```ts theme={null}
{
  currentField: string;
  isValid: boolean;
}
```

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

```ts theme={null}
// GET /api/forms/[formId]/submissions
// Returns all submissions for a form
// Each submission: { id, contactId, data: JSON, createdAt }
```

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

***

## Prisma Indexes Used

| Table            | Index            | Used by                      |
| ---------------- | ---------------- | ---------------------------- |
| `Form`           | `organizationId` | List forms                   |
| `FormSubmission` | `formId`         | List submissions per form    |
| `FormSubmission` | `contactId`      | Contact's submission history |

***

## 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.
