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

# Forms API

> Generate and manage AI-powered forms using Weavely's Forms API. Create, retrieve, and customize form data easily.

<span className="weavely-jsonld" hidden>
  {`{
    "@context": "https://schema.org",
    "@graph": [
    {
      "@type": "WebPage",
      "@id": "https://help.weavely.ai/developers/forms",
      "url": "https://help.weavely.ai/developers/forms",
      "name": "Forms API",
      "description": "Endpoints for retrieving and managing forms.",
      "inLanguage": "en",
      "isPartOf": {
        "@type": "WebSite",
        "@id": "https://help.weavely.ai/#website",
        "name": "Weavely Help Center"
      },
      "breadcrumb": {
        "@type": "BreadcrumbList",
        "itemListElement": [
          { "@type": "ListItem", "position": 1, "name": "Developers", "item": "https://help.weavely.ai/developers" },
          { "@type": "ListItem", "position": 2, "name": "Forms API", "item": "https://help.weavely.ai/developers/forms" }
        ]
      },
      "publisher": { "@id": "https://www.weavely.ai/#org" }
    },
    {
      "@type": "TechArticle",
      "headline": "Forms API",
      "description": "API documentation for developers — learn how to generate and retrieve form data via Weavely's endpoints.",
      "inLanguage": "en",
      "mainEntityOfPage": { "@id": "https://help.weavely.ai/developers/forms" },
      "author": { "@id": "https://www.weavely.ai/#org" },
      "publisher": { "@id": "https://www.weavely.ai/#org" }
    },
    {
      "@type": "Organization",
      "@id": "https://www.weavely.ai/#org",
      "name": "WEAVE.LY BV",
      "url": "https://www.weavely.ai",
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "Herbert Hooverlaan 141",
        "addressLocality": "Schaarbeek",
        "postalCode": "1030",
        "addressCountry": "BE"
      },
      "identifier": "BE0773.878.569",
      "sameAs": [
        "https://www.instagram.com/weavely.ai/",
        "https://www.linkedin.com/company/weave-ly/",
        "https://www.youtube.com/@weavely"
      ]
    }
    ]
    }`}
</span>

## Generate form

> POST api.weavely.ai/v1/forms/generate

### Request

*No authentication header is required for this endpoint.*

#### Body

<ParamField body="name" type="string">
  A friendly name for the form (optional).
</ParamField>

<ParamField body="prompt" type="string" required>
  A natural-language description of the form to generate\
  e.g. `"A simple contact form in Flemish"`.
</ParamField>

<ParamField body="files" type="array[object]">
  Optional array of the following objects:

  ```json theme={null}
  {
  mimeType: file.mimeType,  // e.g., "image/png", "application/pdf"
  data: file.data // file encoded as base64
  }
  ```
</ParamField>

### Response

<ResponseField name="url" type="string">
  The URL that opens the generated form in the Weavely editor.
</ResponseField>

## Get form fields

> GET api.weavely.ai/v1/forms/\[formId]/fields

### Request

#### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer <token>`\
  Your personal token.
</ParamField>

#### Path Parameters

<ParamField path="formId" type="string" required>
  The unique identifier of the form.
</ParamField>

### Response

<ResponseField name="formId" type="string">
  The form's unique identifier.
</ResponseField>

<ResponseField name="version" type="string|null">
  The timestamp of the published version, or `null` if the form has no published version.
</ResponseField>

<ResponseField name="fields" type="array">
  The list of fields in the form.
</ResponseField>

Each field object includes:

<ResponseField name="id" type="string">
  The field's unique identifier.
</ResponseField>

<ResponseField name="label" type="string">
  The field's display label.
</ResponseField>

<ResponseField name="type" type="string">
  The type of the field (e.g., `text`, `datetime`, etc.).
</ResponseField>

## Get form specification

> GET api.weavely.ai/forms/:id/client

Retrieves the complete specification of a published form.

### Request

*No authentication required for this endpoint.*

#### Path Parameters

<ParamField path="id" type="string" required>
  The unique identifier (UUID) of the form.
</ParamField>

### Response

<ResponseField name="id" type="string">
  The form's unique identifier.
</ResponseField>

<ResponseField name="name" type="string">
  The form's display name.
</ResponseField>

<ResponseField name="plan" type="string">
  The Weavely plan tier (e.g., "pro", "free").
</ResponseField>

<ResponseField name="settings" type="object">
  Form configuration including access, general, notifications, and security settings.
</ResponseField>

<ResponseField name="formJSON" type="object">
  Complete form structure with all pages and elements.

  See **Form Structure Reference** below for complete specifications.
</ResponseField>

<ResponseField name="themeJSON" type="object">
  Complete theme configuration.

  See **Theme Configuration Reference** below for all available options.
</ResponseField>

<ResponseField name="variables" type="array">
  Auto-generated variables for each input field in the form.

  Each variable includes `id` (pattern: `field:{elementId}`), `type`, `label`, and `dataType`.
</ResponseField>

<ResponseField name="logicRules" type="array">
  Conditional logic rules configured for the form.

  See **Logic Rules Reference** below for structure.
</ResponseField>

<ResponseField name="eventTriggers" type="array">
  Event-based triggers configured for the form.

  See **Event Triggers Reference** below for structure.
</ResponseField>

<ResponseField name="calculatedValues" type="array">
  Calculated field values configured for the form.
</ResponseField>

<ResponseField name="pageAttributes" type="object">
  Page metadata for SEO and social sharing (icon, title, description, openGraphImage).
</ResponseField>

<ResponseField name="i18n" type="object">
  Internationalization settings including language code and UI translations.
</ResponseField>

<Note>
  This endpoint returns the complete published form specification. The form must be published for this endpoint to return data — unpublished forms will return a 400 Bad Request error.
</Note>

## Create form

> POST api.weavely.ai/v1/forms

Creates a new form from a complete form specification.

### Request

#### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer <token>`\
  Your personal token.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

#### Body

The request body is a complete form specification with the following structure:

<ParamField body="name" type="string">
  Display name for the form shown in the Weavely dashboard.
</ParamField>

<ParamField body="teamId" type="string" required>
  The UUID of the team to associate this form with.
</ParamField>

<ParamField body="publish" type="boolean">
  Set to `true` to publish the form immediately. Without this, the form is created as a draft and won't be accessible at its public URL.
</ParamField>

<ParamField body="formJSON" type="object" required>
  The form structure containing all pages and elements.

  ```json theme={null}
  {
    "pages": [
      {
        "id": "string",
        "name": "string",
        "type": "form-page" | "ending-page",
        "elements": [...]
      }
    ]
  }
  ```

  See **Form Structure Reference** below for complete element types and configurations.
</ParamField>

<ParamField body="themeJSON" type="object" required>
  The form's visual theme configuration.

  ```json theme={null}
  {
    "name": "string",
    "font": {...},
    "logo": {...},
    "colors": {...},
    "layout": {...},
    "visual": {...}
  }
  ```

  See **Theme Configuration Reference** below for all available options.
</ParamField>

<ParamField body="settings" type="object">
  Optional form settings.

  ```json theme={null}
  {
    "type": "quiz",
    "quiz": {
      "instantFeedback": boolean
    },
    "access": {
      "stopSubmissions": boolean
    },
    "general": {
      "showProgressBar": boolean,
      "autoSaveProgress": boolean,
      "showValidationErrors": boolean
    },
    "notifications": {
      "submissionEmail": {...},
      "confirmationEmail": {...}
    },
    "i18n": {
      "language": "string"
    }
  }
  ```

  See **Quiz Mode Reference** below for quiz-specific settings. See **Internationalisation Reference** for supported language codes.
</ParamField>

<ParamField body="logicRules" type="array">
  Optional conditional logic rules.

  Each rule defines conditions and actions to execute based on field values.

  See **Logic Rules Reference** below for complete documentation.
</ParamField>

<ParamField body="eventTriggers" type="array">
  Optional event-based triggers.

  Triggers execute actions in response to form events (e.g., form submission, page load).

  See **Event Triggers Reference** below for available triggers and actions.
</ParamField>

<ParamField body="calculatedValues" type="array">
  Optional calculated field values.

  (Currently undocumented - reserved for future use)
</ParamField>

<ParamField body="pageAttributes" type="object">
  Optional page-specific attributes.

  (Currently undocumented - reserved for future use)
</ParamField>

### Response

<ResponseField name="id" type="string">
  The unique identifier (UUID) of the created form.
</ResponseField>

<ResponseField name="url" type="string">
  Direct link to the form. Only valid if publish was set to true in request body

  Format: `https://forms.weavely.ai/{formId}`
</ResponseField>

<ResponseField name="editor" type="string">
  Direct link to edit the form.

  Format: `https://forms.weavely.ai/editor/{formId}`
</ResponseField>

### Example

**Request:**

```bash theme={null}
curl -X POST https://api.weavely.ai/v1/forms \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wvy_your_token_here" \
  -d '{
    "name": "Contact Form",
    "teamId": "your-team-uuid",
    "publish": true,
    "formJSON": {
      "pages": [{
        "id": "page-1",
        "name": "Contact",
        "type": "form-page",
        "elements": [
          {
            "id": "name",
            "type": "input-text",
            "label": "Your Name",
            "settings": { "required": true },
            "placeholder": "Enter your name..."
          }
        ]
      }]
    },
    "themeJSON": {
      "name": "Nova",
      "colors": {
        "primary": "#6c5ce7",
        "background": "#FFFFFF"
      }
    }
  }'
```

**Response:**

```json theme={null}
{
  "id": "8e2d178f-4e9c-4fe3-9619-e44412bf7ba1",
  "editor": "https://forms.weavely.ai/editor/8e2d178f-4e9c-4fe3-9619-e44412bf7ba1",
  "url": "https://forms.weavely.ai/8e2d178f-4e9c-4fe3-9619-e44412bf7ba1"
}
```

***

## Update form

> POST api.weavely.ai/v1/forms/:id

Updates an existing form. This is a partial update endpoint — only the fields provided in the request body will be updated. All omitted fields remain unchanged.

### Request

#### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer <token>`\
  Your personal token.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

#### Path Parameters

<ParamField path="id" type="string" required>
  The unique identifier (UUID) of the form to update.
</ParamField>

#### Body

All body parameters are optional. Only include the fields you want to update. Omitted fields will remain unchanged.

<ParamField body="name" type="string">
  Update the form's name.
</ParamField>

<ParamField body="formJSON" type="object">
  Update the form structure containing pages and elements.

  ```json theme={null}
    {
      "pages": [
        {
          "id": "string",
          "name": "string",
          "type": "form-page" | "ending-page",
          "elements": [...]
        }
      ]
    }
  ```

  See **Form Structure Reference** below for complete element types and configurations.
</ParamField>

<ParamField body="themeJSON" type="object">
  Update the form's visual theme configuration.

  ```json theme={null}
    {
      "name": "string",
      "font": {...},
      "logo": {...},
      "colors": {...},
      "layout": {...},
      "visual": {...}
    }
  ```

  See **Theme Configuration Reference** below for all available options.
</ParamField>

<ParamField body="settings" type="object">
  Update form settings.

  ```json theme={null}
    {
      "access": {
        "stopSubmissions": boolean
      },
      "general": {
        "showProgressBar": boolean,
        "autoSaveProgress": boolean
      },
      "notifications": {
        "submissionEmail": {...},
        "confirmationEmail": {...}
      }
    }
  ```
</ParamField>

<ParamField body="logicRules" type="array">
  Update conditional logic rules.

  Each rule defines conditions and actions to execute based on field values.

  See **Logic Rules Reference** below for complete documentation.
</ParamField>

<ParamField body="eventTriggers" type="array">
  Update event-based triggers.

  Triggers execute actions in response to form events (e.g., form submission, page load).

  See **Event Triggers Reference** below for available triggers and actions.
</ParamField>

<ParamField body="calculatedValues" type="array">
  Update calculated field values.

  (Currently undocumented - reserved for future use)
</ParamField>

### Response

<ResponseField name="id" type="string">
  The unique identifier (UUID) of the updated form.
</ResponseField>

### Examples

**Update only the form name:**

```bash theme={null}
curl -X POST https://api.weavely.ai/v1/forms/e2fdec38-130c-4e2c-b0c7-4f238206c9ce \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wvy_your_token_here" \
  -d '{
    "name": "Updated Contact Form"
  }'
```

**Response:**

```json theme={null}
{
  "id": "e2fdec38-130c-4e2c-b0c7-4f238206c9ce"
}
```

***

<Note>
  This endpoint performs a **partial update**. Only the top-level fields you include in the request body will be modified. All other fields remain unchanged. This allows you to update specific parts of a form without needing to send the entire form structure.
</Note>

<Warning>
  When updating nested objects like `themeJSON` or `settings`, the entire object at that level is replaced. For example, if you update `themeJSON.colors`, make sure to include all color properties you want to keep, as the entire `colors` object will be replaced.
</Warning>

## Form Structure Reference

### Element Types

Weavely forms support 26 element types organized into categories:

**Input Elements:**

* `input-text` - Single-line text input
* `input-number` - Numeric input
* `input-email` - Email address input with validation
* `input-phone-number` - Phone number input
* `input-url` - URL input with validation
* `input-time` - Time picker
* `input-date` - Date picker
* `text-area` - Multi-line text input
* `input-file` - File upload

**Choice Elements:**

* `checkbox-buttons` - Multiple selection checkboxes
* `radio-buttons` - Single selection radio buttons
* `dropdown` - Dropdown select menu
* `image-choice` - Image-based selection
* `checkbox` - Single checkbox (yes/no)
* `matrix` - Grid of choices (rows × columns)
* `ranking` - Drag-and-drop ranking

**Rating Elements:**

* `star-rating` - Star-based rating (customizable number of stars)
* `scale-rating` - Numeric scale rating
* `range-slider` - Slider with min/max values

**Display Elements:**

* `heading` - Heading text
* `paragraph` - Paragraph text
* `image` - Image display
* `embed-html` - Custom HTML embed
* `embed-audio` - Audio player
* `embed-video` - Video embed

**Special Elements:**

* `signature` - Digital signature capture

***

### Element Specifications

<Accordion title="input-text">
  **Single-line text input**

  ```json theme={null}
  {
    "type": "input-text",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-number">
  **Numeric input only**

  ```json theme={null}
  {
    "type": "input-number",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-email">
  **Email input with validation**

  ```json theme={null}
  {
    "type": "input-email",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-phone-number">
  **Phone number input**

  ```json theme={null}
  {
    "type": "input-phone-number",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-url">
  **URL input with validation**

  ```json theme={null}
  {
    "type": "input-url",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-time">
  **Time picker input**

  ```json theme={null}
  {
    "type": "input-time",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-date">
  **Date picker input**

  ```json theme={null}
  {
    "type": "input-date",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="text-area">
  **Multi-line text input**

  ```json theme={null}
  {
    "type": "text-area",
    "label": "string",
    "settings": {
      "required": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="input-file">
  **File upload input**

  ```json theme={null}
  {
    "type": "input-file",
    "label": "string",
    "settings": {
      "required": boolean,
      "maxFiles": number,
      "maxFileSize": number,
      "allowedFileType": "string"
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="checkbox-buttons">
  **Multiple selection checkboxes**

  ```json theme={null}
  {
    "type": "checkbox-buttons",
    "label": "string",
    "settings": {
      "required": boolean,
      "randomize": boolean,
      "allowOtherOption": boolean,
      "otherOptionLabel": "string",
      "options": [
        {
          "label": "string",
          "value": "string"
        }
      ]
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="radio-buttons">
  **Single selection radio group**

  ```json theme={null}
  {
    "type": "radio-buttons",
    "label": "string",
    "settings": {
      "required": boolean,
      "randomize": boolean,
      "allowOtherOption": boolean,
      "otherOptionLabel": "string",
      "options": [
        {
          "label": "string",
          "value": "string"
        }
      ]
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="dropdown">
  **Dropdown select menu**

  ```json theme={null}
  {
    "type": "dropdown",
    "label": "string",
    "settings": {
      "required": boolean,
      "randomize": boolean,
      "options": [
        {
          "label": "string",
          "value": "string"
        }
      ]
    },
    "description": "string"
  }
  ```
</Accordion>

<Accordion title="image-choice">
  **Image-based selection**

  ```json theme={null}
  {
    "type": "image-choice",
    "label": "string",
    "settings": {
      "required": boolean,
      "multiple": boolean,
      "randomize": boolean,
      "imageWidth": number,
      "imageHeight": number,
      "imageFit": "string",
      "options": [
        {
          "label": "string",
          "value": "string",
          "data": { "url": "string" } | null
        }
      ]
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="checkbox">
  **Single checkbox (yes/no)**

  ```json theme={null}
  {
    "type": "checkbox",
    "label": "string",
    "settings": {
      "required": boolean,
      "default": boolean
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="matrix">
  **Grid of choices (rows × columns)**

  ```json theme={null}
  {
    "type": "matrix",
    "label": "string",
    "fields": [
      {
        "id": "string (UUID)",
        "type": "matrix-field",
        "label": "string",
        "settings": {
          "required": boolean,
          "multiple": boolean,
          "options": [
            {
              "label": "string",
              "value": "string"
            }
          ]
        },
        "description": "string",
        "placeholder": "string"
      }
    ],
    "settings": {
      "required": boolean,
      "multiple": boolean,
      "options": [
        {
          "label": "string",
          "value": "string"
        }
      ]
    },
    "description": "string"
  }
  ```
</Accordion>

<Accordion title="ranking">
  **Drag-and-drop ranking**

  ```json theme={null}
  {
    "type": "ranking",
    "label": "string",
    "settings": {
      "required": boolean,
      "options": [
        {
          "label": "string",
          "value": "string"
        }
      ]
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="star-rating">
  **Star-based rating**

  ```json theme={null}
  {
    "type": "star-rating",
    "label": "string",
    "settings": {
      "required": boolean,
      "stars": number,
      "icon": "string"
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="scale-rating">
  **Numeric scale rating**

  ```json theme={null}
  {
    "type": "scale-rating",
    "label": "string",
    "settings": {
      "required": boolean,
      "scales": number
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="range-slider">
  **Slider with min/max values**

  ```json theme={null}
  {
    "type": "range-slider",
    "label": "string",
    "settings": {
      "min": number,
      "max": number,
      "step": number
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

<Accordion title="heading">
  **Heading text**

  ```json theme={null}
  {
    "type": "heading",
    "label": "string"
  }
  ```
</Accordion>

<Accordion title="paragraph">
  **`Paragraph text. Supports HTML markup and variable piping via {{variable_id}} syntax.`**

  ```json theme={null}
  {
    "type": "paragraph",
    "label": "string"
  }
  ```
</Accordion>

<Accordion title="image">
  **Image display**

  ```json theme={null}
  {
    "type": "image",
    "settings": {
      "src": "string (URL)"
    }
  }
  ```
</Accordion>

<Accordion title="embed-html">
  **Custom HTML embed**

  ```json theme={null}
  {
    "type": "embed-html",
    "settings": {
      "codeSnippet": "string"
    }
  }
  ```
</Accordion>

<Accordion title="embed-audio">
  **Audio player**

  ````json theme={null}
  {
    "type": "embed-audio",
    "settings": {
      "url": "string"
    }
  }
  </Accordion>

  <Accordion title="embed-video">
  **Video embed**

  ```json
  {
    "type": "embed-video",
    "settings": {
      "url": "string"
    }
  }
  ````
</Accordion>

<Accordion title="signature">
  **Digital signature capture**

  ```json theme={null}
  {
    "type": "signature",
    "label": "string",
    "settings": {
      "fileFormat": "string"
    },
    "description": "string",
    "placeholder": "string"
  }
  ```
</Accordion>

***

## Theme Configuration Reference

The `themeJSON` object controls all visual styling for the form. It contains the following sections: `name`, `font`, `logo`, `colors`, `layout`, `visual`, and `components`.

### Theme Presets

Weavely includes 7 preset themes. Set via the `name` field — the preset provides default values for colors, fonts, and components. Any explicit values you set will override the preset defaults.

* `Nova`
* `Retro`
* `Dawn`
* `Dusk`
* `Frost`
* `Ember`
* `Glass`

### Font

Controls typography for body text and headings independently.

```json theme={null}
"font": {
  "text": {
    "size": "16px",
    "family": "Plus Jakarta Sans"
  },
  "headings": {
    "size": "32px",
    "family": "Plus Jakarta Sans"
  }
}
```

You can also set the font family globally using a shorthand:

```json theme={null}
"font": {
  "family": "Poppins"
}
```

### Logo

Optional logo image displayed on the form.

```json theme={null}
"logo": {
  "src": "string (URL)" | null,
  "variables": {
    "width": "40px",
    "justifySelf": "center"
  }
}
```

Set `src` to an image URL, or `null` for no logo.

### Colors

Full color palette for the form. All values are hex codes.

```json theme={null}
"colors": {
  "primary": "#6c5ce7",
  "background": "#FFFFFF",
  "text": "#000000",
  "question": "#000000",
  "answer": "#000000",
  "secondary": "#222222",
  "surface": "#F7F8FA",
  "border": "#dedfe0",
  "error": "#FF0000"
}
```

| Color        | Controls                        |
| ------------ | ------------------------------- |
| `primary`    | Buttons, accents, active states |
| `background` | Page/form background            |
| `text`       | General body text               |
| `question`   | Field labels / question text    |
| `answer`     | User input text                 |
| `secondary`  | Secondary UI elements           |
| `surface`    | Input field backgrounds         |
| `border`     | Input borders, dividers         |
| `error`      | Validation error messages       |

You can provide only a subset of colors (e.g. just `primary` and `background`) and the theme preset will fill in the rest.

### Layout

Controls where the visual (image or color) appears relative to the form.

```json theme={null}
"layout": {
  "type": "right"
}
```

| Type      | Description                                                 |
| --------- | ----------------------------------------------------------- |
| `under`   | Visual is placed behind the form as a full-page background  |
| `left`    | Visual is displayed as a left side panel, form on the right |
| `right`   | Visual is displayed as a right side panel, form on the left |
| `clean`   | No visual — form only                                       |
| `over`    | Visual overlays the form area                               |
| `through` | Visual bleeds through the form                              |

### Visual

The background visual — either an image or a solid color. Works together with `layout` to determine how the visual is positioned.

**Image visual:**

```json theme={null}
"visual": {
  "type": "image",
  "value": "string (image URL)",
  "variables": {
    "size": "cover",
    "repeat": "no-repeat",
    "position": "center"
  }
}
```

**Color visual:**

```json theme={null}
"visual": {
  "type": "color",
  "value": "#5a3131",
  "variables": {
    "size": "cover",
    "repeat": "no-repeat",
    "position": "center"
  }
}
```

The `variables` object uses CSS-like properties and defaults to `size: "cover"`, `repeat: "no-repeat"`, `position: "center"` for both types.

**Common combinations:**

* Side image: `layout.type: "right"` (or `"left"`) + `visual.type: "image"` — displays a photo alongside the form
* Background color: `layout.type: "under"` + `visual.type: "color"` — fills the page behind the form with a solid color
* Background image: `layout.type: "under"` + `visual.type: "image"` — fills the page behind the form with an image
* No visual: `layout.type: "clean"` — form only, no visual element

### Components

Controls form layout, input style, button style, and question weight.

```json theme={null}
"components": {
  "form": {
    "variables": {
      "gap": "30px",
      "maxWidth": "700px",
      "textAlign": "left"
    }
  },
  "input": {
    "preset": "default"
  },
  "button": {
    "preset": "default",
    "hoverAnimation": {
      "preset": "grow"
    }
  },
  "question": {
    "variables": {
      "fontWeight": "500"
    }
  }
}
```

**Input presets:**

* `default` — Rounded input styling
* `square` — Square input styling

**Button presets:**

* `default` — Rounded button styling
* `square` — Square button styling

**Hover animation presets:**

* `default` — Default hover animation
* `grow` — Grow effect on hover

### Complete themeJSON Example

```json theme={null}
{
  "name": "Nova",
  "font": {
    "text": { "size": "16px", "family": "Plus Jakarta Sans" },
    "headings": { "size": "32px", "family": "Plus Jakarta Sans" }
  },
  "logo": {
    "src": null,
    "variables": { "width": "40px", "justifySelf": "center" }
  },
  "colors": {
    "primary": "#6c5ce7",
    "background": "#FFFFFF",
    "text": "#000000",
    "question": "#000000",
    "answer": "#000000",
    "secondary": "#222222",
    "surface": "#F7F8FA",
    "border": "#dedfe0",
    "error": "#FF0000"
  },
  "layout": { "type": "right" },
  "visual": {
    "type": "image",
    "value": "https://example.com/your-image.jpg",
    "variables": { "size": "cover", "repeat": "no-repeat", "position": "center" }
  },
  "components": {
    "form": { "variables": { "gap": "30px", "maxWidth": "700px", "textAlign": "left" } },
    "input": { "preset": "default" },
    "button": { "preset": "default", "hoverAnimation": { "preset": "grow" } },
    "question": { "variables": { "fontWeight": "500" } }
  }
}
```

***

## Quiz Mode Reference

Quiz mode turns a form into a scored assessment.

### Enabling Quiz Mode

Set `settings.type` to `"quiz"` and optionally configure quiz-specific settings:

```json theme={null}
{
  "settings": {
    "type": "quiz",
    "quiz": {
      "instantFeedback": true
    }
  }
}
```

| Setting                | Type     | Description                                                                              |
| ---------------------- | -------- | ---------------------------------------------------------------------------------------- |
| `type`                 | `"quiz"` | Activates quiz mode. Omit for standard form behaviour                                    |
| `quiz.instantFeedback` | boolean  | When `true`, shows correct/incorrect feedback after each question rather than at the end |

### Quiz-Compatible Element Types

Only the following element types support quiz scoring:

* `radio-buttons`
* `checkbox-buttons`
* `input-text`
* `dropdown`

### The `quiz` Property

Add a `quiz` object to any compatible element to define its correct answer and score:

```json theme={null}
{
  "id": "string (UUID)",
  "type": "radio-buttons",
  "label": "What is the capital of France?",
  "settings": { ... },
  "quiz": {
    "score": 1,
    "answer": "Paris"
  }
}
```

| Field    | Type             | Description                                                                                                                                                                                                        |
| -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `score`  | number           | Points awarded for a correct answer. Can be any positive number — questions can be weighted differently                                                                                                            |
| `answer` | string           | Correct answer for single-select elements (`radio-buttons`, `input-text`, `dropdown`). For `radio-buttons` and `dropdown`, must match the option's `value` exactly. For `input-text`, matching is case-insensitive |
| `answer` | array of strings | All correct answers for `checkbox-buttons` — respondent must select exactly this set of values                                                                                                                     |

### Quiz Variables

When quiz mode is active, four variables are automatically available for use in logic rules and answer piping:

| Variable ID                 | Description                                             |
| --------------------------- | ------------------------------------------------------- |
| `quiz:quiz-score`           | Total points scored by the respondent                   |
| `quiz:max-score`            | Maximum possible score (sum of all `quiz.score` values) |
| `quiz:correct-answers`      | Number of questions answered correctly                  |
| `quiz:total-quiz-questions` | Total number of quiz questions in the form              |

### Score-Based Conditional Endings

Use quiz variables in logic rules to direct respondents to different ending pages based on their score:

```json theme={null}
{
  "id": "string (UUID)",
  "name": "High score ending",
  "logicalOperator": "any",
  "conditions": [
    {
      "id": "string (UUID)",
      "variable": "quiz:quiz-score",
      "operator": "greaterThanOrEqual",
      "value": "5"
    }
  ],
  "actions": [
    {
      "id": "string (UUID)",
      "name": "setEnding",
      "data": {
        "elementId": "string (ending-page UUID)"
      }
    }
  ]
}
```

All standard logic rule actions are available (`hideElement`, `showElement`, `hidePage`, `skipToPage`, `setEnding`).

### Showing Results to Respondents

Pipe quiz variables into paragraph labels using `{{variable_id}}` syntax. Element labels support HTML markup:

```html theme={null}
<div>Thanks for completing the quiz!</div>
<div><br></div>
<div>Score: {{quiz:quiz-score}} / {{quiz:max-score}}</div>
<div>Correct answers: {{quiz:correct-answers}} / {{quiz:total-quiz-questions}}</div>
```

***

## Payment Mode Reference

Payment mode turns a form into a payment collection form.

### Enabling Payment Mode

Set `settings.type` to `"payment"`:

```json theme={null}
{
  "settings": {
    "type": "payment"
  }
}
```

| Setting | Type        | Description                                              |
| ------- | ----------- | -------------------------------------------------------- |
| `type`  | `"payment"` | Activates payment mode. Omit for standard form behaviour |

<Note>
  Payment forms require additional setup in the Weavely dashboard (Stripe integration, pricing, etc.) before they can accept payments. The API call only flags the form as a payment form — it does not configure the payment provider.
</Note>

***

## Logic Rules Reference

Logic rules enable conditional behavior based on field values.

### Structure

```json theme={null}
{
  "id": "string (UUID)",
  "name": "string",
  "logicalOperator": "any" | "all",
  "conditions": [{
    "id": "string (UUID)",
    "variable": "field:{elementId}",
    "operator": "isEmpty" | "isEqual" | "contains" | ...,
    "value": null | string | number
  }],
  "actions": [{
    "id": "string (UUID)",
    "name": "hideElement" | "showElement" | "skipToPage" | ...,
    "data": { "elementId": "string (UUID)" }
  }]
}
```

### Logical Operators

* `any` - OR logic: trigger actions if ANY condition is true
* `all` - AND logic: trigger actions if ALL conditions are true

### Condition Operators

**Universal (all field types):**

* `isEmpty` - Check if field is empty/null
* `isNotEmpty` - Check if field has a value
* `isEqual` - Check if field equals a specific value
* `isNotEqual` - Check if field does not equal a specific value

**String operators (text fields):**

* `contains` - Check if field contains a substring
* `doesNotContain` - Check if field does not contain a substring
* `startsWith` - Check if field starts with a string
* `endsWith` - Check if field ends with a string

**Numeric operators (number fields and quiz variables):**

* `lessThan` - Check if field is less than a value
* `lessThanOrEqual` - Check if field is less than or equal to a value
* `greaterThan` - Check if field is greater than a value
* `greaterThanOrEqual` - Check if field is greater than or equal to a value

### Available Actions

* `hideElement` - Hide a specific element
* `showElement` - Show a specific element
* `hidePage` - Hide a specific page
* `skipToPage` - Navigate to a specific page
* `setEnding` - Set which ending page to display

***

## Event Triggers Reference

Event triggers execute actions in response to form events.

### Structure

```json theme={null}
{
  "id": "string (UUID)",
  "name": "string",
  "trigger": {
    "name": "formSubmitted" | "formLoaded" | "formPageShown"
  },
  "actions": [{
    "id": "string (UUID)",
    "name": "openUrl" | "restartForm",
    "data": { "url": "string" }
  }]
}
```

### Available Triggers

* `formSubmitted` - Triggered when form is successfully submitted
* `formLoaded` - Triggered when form initially loads
* `formPageShown` - Triggered when a form page is displayed

### Available Actions

* `openUrl` - Redirect to a URL
  * Requires `data.url` field
* `restartForm` - Restart the form from the beginning
  * No additional data required

***

## Internationalisation Reference

Set the form's language via `settings.i18n.language`. The server automatically injects the correct system message translations (button labels, placeholders, error messages) for the chosen language.

```json theme={null}
{
  "settings": {
    "i18n": {
      "language": "fr"
    }
  }
}
```

### Supported Languages

| Language              | Code      |
| --------------------- | --------- |
| Arabic                | `ar`      |
| Catalan               | `ca`      |
| Chinese (Simplified)  | `zh-Hans` |
| Chinese (Traditional) | `zh-Hant` |
| Croatian              | `hr`      |
| Czech                 | `cs`      |
| Danish                | `da`      |
| Dutch                 | `nl`      |
| English               | `en`      |
| Estonian              | `et`      |
| Finnish               | `fi`      |
| French                | `fr`      |
| German (Formal)       | `de`      |
| German (Informal)     | `di`      |
| Greek                 | `el`      |
| Hebrew                | `he`      |
| Hindi                 | `hi`      |
| Hungarian             | `hu`      |
| Indonesian            | `id`      |
| Italian               | `it`      |
| Japanese              | `ja`      |
| Korean                | `ko`      |
| Norwegian             | `no`      |
| Polish                | `pl`      |
| Portuguese            | `pt`      |
| Russian               | `ru`      |
| Spanish               | `es`      |
| Swedish               | `sv`      |
| Turkish               | `tr`      |
| Ukrainian             | `uk`      |
| Vietnamese            | `vi`      |

Note: German uses custom codes — `de` for formal ("Sie") and `di` for informal ("du").
