Standard call shape (every curl below). The <AUTH> placeholder is shorthand for Authorization: Bearer <TOKEN> only. Body-bearing requests also need Content-Type: application/json.
Create a form on a Wix site that appears in the Forms & Submissions dashboard. The form collects visitor information (e.g., name, email) and can automatically upsert contacts on submission.
Call the Create Form endpoint with the wix.form_app.form namespace. The Wix Forms app (appDefId: 14ce1214-b278-a7e4-1373-00cebd1bef7c) is usually already installed on sites.
The response includes the created form with its id. Store this ID to manage the form later.
Verify the form in the dashboard: https://manage.wix.com/dashboard/{siteId}/forms
id fields (for formFields, steps) and all fieldId references in the layout must be valid UUIDs. Generate fresh UUIDs for each form you create — do not reuse the example UUIDs above.id and a unique target value. The target is used to map submissions to contact fields.identifier must be a recognized Wix value. Custom identifiers like "product_name" or "color_preference" will cause the field to be silently dropped from the form — no error is thrown. For any generic/custom text field, use "TEXT_INPUT" as the identifier and set the display name via the label property in textInputOptions."format": "UNKNOWN_FORMAT". For email fields, use "format": "EMAIL". For phone fields, use "format": "PHONE". Valid format values: UNKNOWN_FORMAT, DATE, TIME, DATE_TIME, EMAIL, URL, UUID, PHONE, URI, HOSTNAME, COLOR_HEX, CURRENCY, LANGUAGE, DATE_OPTIONAL_TIME.DISPLAY field with identifier: "SUBMIT_BUTTON".maximum number of forms reached), forcing you to GET the form list and DELETE the test forms before the real create can succeed. Assemble all fields (including any RADIO_GROUP/DROPDOWN per § "Choice fields") and POST once.| Identifier | componentType | format | Use case |
|---|---|---|---|
TEXT_INPUT | TEXT_INPUT | UNKNOWN_FORMAT | Generic single-line text (use label for display name) |
CONTACTS_FIRST_NAME | TEXT_INPUT | UNKNOWN_FORMAT | Contact first name |
CONTACTS_LAST_NAME | TEXT_INPUT | UNKNOWN_FORMAT | Contact last name |
CONTACTS_EMAIL | TEXT_INPUT | EMAIL | Contact email |
CONTACTS_PHONE | TEXT_INPUT | PHONE | Contact phone |
SUBMIT_BUTTON | N/A (DISPLAY field) | N/A | Submit button |
TEXT_INPUT | RADIO_GROUP | UNKNOWN_FORMAT | Single-choice from a fixed list (radio buttons) — see § "Choice fields" |
TEXT_INPUT | DROPDOWN | UNKNOWN_FORMAT | Single-choice from a fixed list (dropdown) — same shape as RADIO_GROUP |
Note: LONG_TEXT_INPUT is not supported as a componentType via REST — it throws INVALID_ARGUMENT. Use TEXT_INPUT for all text fields.
A single-choice field (radio buttons or a dropdown — e.g. an RSVP "Will you attend?") is a STRING input field, not a separate field type. It uses identifier: "TEXT_INPUT", inputType: "STRING", and sets componentType to RADIO_GROUP (or DROPDOWN) inside stringOptions. Two things must agree or the field breaks:
stringOptions.validation.enum must list every option value (an empty enum is for free-text only).stringOptions.radioGroupOptions.options[] carries the rendered choices — each option needs its own UUID id, a value, and a label. (For DROPDOWN, use dropdownOptions with the same {id, value, label} shape.)CRITICAL — silent fallback to TEXT_INPUT. If radioGroupOptions is missing/malformed (wrong key like choices instead of options, an option missing its id, or an empty validation.enum), the API does not error — it silently creates the field as a plain TEXT_INPUT. If a choice field renders as a text box, this is why. Build it correctly on the first call; do not probe.
numberOfColumns is a string enum ("ONE", "TWO", "THREE") controlling the radio layout — omit it and the field still works (defaults to one column).
The steps[].layout.large.items array controls how fields are positioned:
row and column set the position (0-based grid)width sets the column span (max 12 for full width, 6 for half)height is typically 1The postSubmissionTriggers.upsertContact object maps form field targets to contact fields, so each submission automatically creates or updates a contact. The fieldsMapping keys must match the target values from the form fields.
The Wix Forms app (appDefId: 14ce1214-b278-a7e4-1373-00cebd1bef7c) must be installed on the site. It is usually pre-installed, but if the API returns a "missing installed app" error, install it first using the Install Wix Apps recipe.
| Error | Cause | Fix |
|---|---|---|
Unrecognized value passed for enum | Invalid componentType value (e.g., LONG_TEXT_INPUT) | Use only componentType values from the schema: TEXT_INPUT, RADIO_GROUP, DROPDOWN, DATE_TIME, PHONE_INPUT, DATE_INPUT, TIME_INPUT, DATE_PICKER, PASSWORD |
| Field silently missing from created form | Custom identifier value (e.g., "product_name") | Use a recognized identifier like TEXT_INPUT and set display name via label |
| Choice field rendered as a plain text box | radioGroupOptions/dropdownOptions malformed (wrong key, option missing id, empty validation.enum) — API silently falls back to TEXT_INPUT | Match the § "Choice fields" shape exactly: componentType in stringOptions, options[] each with a UUID id, and validation.enum listing all option values |
maximum number of forms reached / form-cap error | Sites cap at ~4 forms; reached by creating throwaway test forms | GET form-schema-service/v4/forms then DELETE the unwanted forms; build the real form in one call (don't probe) |
Permissions for given namespace not found | wix.form_app.form namespace not active | Ensure the Wix Forms app is installed; try creating a form through the UI first to activate the namespace |
missing installed app | Wix Forms app not installed | Install app 14ce1214-b278-a7e4-1373-00cebd1bef7c via the Install Wix Apps recipe |