> ## 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" | "score-outcome-page" | "match-outcome-page",
        "elements": [...]
      }
    ]
  }
  ```

  See **Form Structure Reference** below for page types, 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" | "score" | "match" | "payment",
    "quiz": {
      "instantFeedback": boolean
    },
    "score": {
      "outcomes": [...]
    },
    "match": {
      "outcomes": [...]
    },
    "access": {
      "stopSubmissions": boolean
    },
    "general": {
      "showProgressBar": boolean,
      "autoSaveProgress": boolean,
      "showValidationErrors": boolean
    },
    "notifications": {
      "submissionEmail": {...},
      "confirmationEmail": {...}
    },
    "i18n": {
      "language": "string"
    }
  }
  ```

  Omit `type` for universal form behaviour. See **Quiz Mode Reference**, **Score Mode Reference**, **Match Mode Reference**, and **Payment Mode Reference** below for mode-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 published form. **Only present when `publish` is set to `true`** in the 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" | "score-outcome-page" | "match-outcome-page",
        "elements": [...]
      }
    ]
  }
  ```

  See **Form Structure Reference** below for page types, 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}
  {
    "type": "quiz" | "score" | "match" | "payment",
    "quiz": {
      "instantFeedback": boolean
    },
    "score": {
      "outcomes": [...]
    },
    "match": {
      "outcomes": [...]
    },
    "access": {
      "stopSubmissions": boolean
    },
    "general": {
      "showProgressBar": boolean,
      "autoSaveProgress": boolean,
      "showValidationErrors": boolean
    },
    "notifications": {
      "submissionEmail": {...},
      "confirmationEmail": {...}
    },
    "i18n": {
      "language": "string"
    }
  }
  ```

  Omit `type` for universal form behaviour. See **Quiz Mode Reference**, **Score Mode Reference**, **Match Mode Reference**, and **Payment Mode Reference** below for mode-specific settings. See **Internationalisation Reference** for supported language codes.
</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

### Page Structure

A form consists of one or more pages. Each page has the following structure:

```json theme={null}
{
  "id": "string (UUID)",
  "name": "string",
  "type": "form-page" | "ending-page" | "score-outcome-page" | "match-outcome-page",
  "elements": [...]
}
```

#### Page Types

| Type                 | Description                                                                                                                                                     |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `form-page`          | A standard page in the form flow. Can contain any element type. Pages are shown in the order they appear in the `pages` array.                                  |
| `ending-page`        | Shown after the form is submitted. Not part of the fillable flow. Used by universal forms and quizzes.                                                          |
| `score-outcome-page` | Shown after submission of a **score form**, based on the respondent's total score. Used instead of ending pages — see **Score Mode Reference** below.           |
| `match-outcome-page` | Shown after submission of a **match form**, based on the respondent's best-matching outcome. Used instead of ending pages — see **Match Mode Reference** below. |

#### Ending Pages

**Every universal form and quiz must have at least one ending page.** Score and match forms use outcome pages instead (one per outcome).

Ending pages and outcome pages can only contain **display elements**:

* `heading`
* `paragraph`
* `image`
* `lottie`
* `embed-html`
* `embed-audio`
* `embed-video`

A form can have **multiple ending pages**. By default, the first ending page is shown after submission. Use the `setEnding` logic rule action to conditionally direct respondents to a different ending page — for example, based on a quiz score or a specific answer (see **Logic Rules Reference** below).

<Warning>
  The API does not validate the presence of an ending page — forms can be created and even published without one. It is the responsibility of the API consumer to always include at least one ending page (or, for score and match forms, one outcome page per outcome) in `formJSON.pages`. The Weavely editor enforces this for forms built in the UI.
</Warning>

### Element Types

Weavely forms support 27 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
* `lottie` - Lottie animation player
* `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="lottie">
  **Lottie animation player**

  ```json theme={null}
  {
    "type": "lottie",
    "settings": {
      "src": "string (URL to Lottie JSON file)",
      "loop": boolean
    }
  }
  ```
</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 theme={null}
  {
    "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 universal 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>
```

***

## Score Mode Reference

Score mode turns a form into a scored assessment that routes respondents to different outcome pages based on their total score — ideal for lead qualification, assessments, and personality-style tests.

### Enabling Score Mode

Set `settings.type` to `"score"` and define the outcomes:

```json theme={null}
{
  "settings": {
    "type": "score",
    "score": {
      "outcomes": [
        {
          "id": "string (UUID)",
          "name": "Cold Lead",
          "pageId": "string (score-outcome-page UUID)",
          "maxScore": 5
        },
        {
          "id": "string (UUID)",
          "name": "Warm Lead",
          "pageId": "string (score-outcome-page UUID)",
          "maxScore": 10
        },
        {
          "id": "string (UUID)",
          "name": "Hot Lead",
          "pageId": "string (score-outcome-page UUID)"
        }
      ]
    }
  }
}
```

### Outcomes

Outcomes divide the total score into contiguous ranges. After submission, the respondent is shown the outcome page whose range their total score falls into.

| Field      | Type          | Description                                                                                                                                                                              |
| ---------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`       | string (UUID) | Unique identifier for the outcome                                                                                                                                                        |
| `name`     | string        | Outcome name. Exposed via the `score:outcome` variable                                                                                                                                   |
| `pageId`   | string (UUID) | The `id` of the `score-outcome-page` to show for this outcome                                                                                                                            |
| `maxScore` | number        | Inclusive upper bound of the outcome's score range. Each range starts where the previous outcome's range ends. **Must be omitted on the last outcome**, which captures all higher scores |

The example above produces the ranges: up to 5 → Cold Lead, 6 to 10 → Warm Lead, 11 or more → Hot Lead.

### Outcome Pages

Score forms use pages of type `score-outcome-page` **instead of** `ending-page`. Define one page per outcome and reference it via the outcome's `pageId`. Like ending pages, score outcome pages can only contain display elements (see **Page Structure** above).

### Scoring Answers

Points are assigned **per option** by adding a `score` property to options. The following element types support option scores:

* `radio-buttons`
* `checkbox-buttons`
* `dropdown`

```json theme={null}
{
  "type": "radio-buttons",
  "label": "How often do you typically wash your car?",
  "settings": {
    "required": true,
    "options": [
      { "label": "Once a week", "value": "Once a week", "score": 3 },
      { "label": "Once a month", "value": "Once a month", "score": 2 },
      { "label": "Rarely", "value": "Rarely", "score": 0 }
    ]
  }
}
```

### Score Variables

When score mode is active, two variables are automatically available:

| Variable ID     | Description                            |
| --------------- | -------------------------------------- |
| `score:total`   | The respondent's total score (number)  |
| `score:outcome` | The name of the matched outcome (text) |

Both can be used in logic rule conditions and piped into element labels using `{{variable_id}}` syntax, e.g. `Your score: {{score:total}}`.

***

## Match Mode Reference

Match mode turns a form into a personality-style quiz that matches respondents to one of several outcomes based on weighted answers — ideal for "Which X are you?" quizzes and product recommenders.

### Enabling Match Mode

Set `settings.type` to `"match"` and define the outcomes:

```json theme={null}
{
  "settings": {
    "type": "match",
    "match": {
      "outcomes": [
        {
          "id": "string (UUID)",
          "name": "Sports Car",
          "pageId": "string (match-outcome-page UUID)"
        },
        {
          "id": "string (UUID)",
          "name": "SUV",
          "pageId": "string (match-outcome-page UUID)"
        }
      ]
    }
  }
}
```

### Outcomes

Unlike score mode, match outcomes have no score ranges. Each answer option contributes weighted points to one or more outcomes, and the outcome with the **highest accumulated total** is matched.

| Field    | Type          | Description                                                               |
| -------- | ------------- | ------------------------------------------------------------------------- |
| `id`     | string (UUID) | Unique identifier for the outcome. Referenced by option `weights`         |
| `name`   | string        | Outcome name. Exposed via the `match:outcome` variable                    |
| `pageId` | string (UUID) | The `id` of the `match-outcome-page` to show when this outcome is matched |

### Outcome Pages

Match forms use pages of type `match-outcome-page` **instead of** `ending-page`. Define one page per outcome and reference it via the outcome's `pageId`. Like ending pages, match outcome pages can only contain display elements (see **Page Structure** above).

### Weighting Answers

Add a `weights` object to each option, mapping **outcome IDs to points**. A single option can contribute to multiple outcomes:

```json theme={null}
{
  "type": "radio-buttons",
  "label": "What's your ideal weekend activity?",
  "settings": {
    "required": true,
    "options": [
      {
        "label": "Racing or a track day",
        "value": "Racing or a track day",
        "weights": {
          "{sports-car-outcome-id}": 3
        }
      },
      {
        "label": "Camping or off-roading",
        "value": "Camping or off-roading",
        "weights": {
          "{pickup-truck-outcome-id}": 2,
          "{suv-outcome-id}": 2
        }
      }
    ]
  }
}
```

The following element types support option weights:

* `radio-buttons`
* `checkbox-buttons`
* `dropdown`

When the form is submitted, the weights of all selected options are added up per outcome. The respondent is shown the outcome page of the highest-scoring outcome.

### Match Variables

When match mode is active, one variable is automatically available:

| Variable ID     | Description                            |
| --------------- | -------------------------------------- |
| `match:outcome` | The name of the matched outcome (text) |

It can be used in logic rule conditions and piped into element labels using `{{variable_id}}` syntax, e.g. `You are: {{match:outcome}}`.

***

## 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 universal 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").
