@wix/agent-skills 1.16.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +18 -3
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +12 -7
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +60 -0
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +6 -6
- package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +187 -0
- package/skills/wix-app/scripts/scan-a11y-code.cjs +777 -0
- package/skills/wix-app/scripts/scan-a11y-eslint.cjs +201 -0
- package/skills/wix-headless/references/CAPABILITIES.md +16 -3
- package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
- package/skills/wix-headless/references/SEED.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +560 -102
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +210 -143
- package/skills/wix-manage/SKILL.md +8 -1
- package/skills/wix-manage/references/accessibility/scan-site-accessibility.md +98 -0
- package/skills/wix-manage/references/analytics/analytics-dashboard-navigation.md +1 -2
- package/skills/wix-manage/references/sites/site-import.md +67 -15
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +1 -1
- package/skills/wix-vibe-headless/SKILL.md +2 -1
- package/skills/wix-vibe-headless/install/deploy.cjs +64 -3
- package/skills/wix-vibe-headless/platforms/base44.md +12 -37
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +1 -2
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +7 -12
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +1 -1
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +5 -3
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +7 -11
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.js +44 -5
|
@@ -1,174 +1,241 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Setup Forms"
|
|
3
|
-
description: Initializes a Wix Forms backend
|
|
3
|
+
description: Initializes a Wix Forms backend with Form Schemas v4 for any visitor-fillable form — contact, survey, application, waitlist or custom data capture — by cleaning the install's sample forms, creating one form schema per requested form, then verifying each renders via the form summary; revises an existing form with `PATCH` + its `revision`. Specifies the *how* (calls + format); which forms and what they collect come from the request.
|
|
4
4
|
---
|
|
5
5
|
**RECIPE**: Business Recipe – Initial Setup for Wix Forms (Form Schemas v4)
|
|
6
6
|
|
|
7
|
-
> **Standard call shape (every curl below).** The `<AUTH>` placeholder is shorthand for
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
> **Standard call shape (every curl below).** The `<AUTH>` placeholder is shorthand for
|
|
8
|
+
> `Authorization: Bearer <TOKEN>` only. Body-bearing requests also need
|
|
9
|
+
> `Content-Type: application/json`. Send `wix-site-id: <SITE_ID>` on every call.
|
|
10
|
+
|
|
11
|
+
A concise checklist for preparing any new Wix site that uses the **Wix Forms** app. Wix Forms backs
|
|
12
|
+
**any form a visitor fills in** — a contact or enquiry form, a signup or waitlist, an application, a
|
|
13
|
+
feedback form or survey, a quote request, an intake or registration questionnaire, or any custom data
|
|
14
|
+
capture. Lead capture is the most common case, not the only one: the schema is a generic
|
|
15
|
+
field-definition store, and whether a submission becomes a CRM contact is just the optional per-field
|
|
16
|
+
`contactMapping` (STEP 2). This recipe is for **initial backend setup ONLY**, not for coding the
|
|
17
|
+
frontend.
|
|
18
|
+
|
|
19
|
+
> **Two boundaries.** Forms does **not** own **RSVP to an event or occasion** (that is the `events`
|
|
20
|
+
> vertical, which ships its own registration form) or the **per-service booking form** (`bookings`).
|
|
21
|
+
> Route there when an event or a bookable service is involved; use `forms` for everything else.
|
|
22
|
+
|
|
23
|
+
> **This recipe is the *how*, not the *what*.** How many forms, and what each one collects, come
|
|
24
|
+
> from the request you're fulfilling. This recipe only specifies the calls and the request format;
|
|
25
|
+
> it does not decide which forms to create.
|
|
26
|
+
|
|
27
|
+
> **API surfaces:** Wix Forms is a **standalone CRM API**. A form **schema** (the field definitions)
|
|
28
|
+
> lives on **Form Schemas v4** at `https://www.wixapis.com/form-schema-service/v4/forms` — docs
|
|
29
|
+
> portal **CRM ▸ Forms ▸ Form Schemas**, *not* Business Solutions. This is **NOT** the
|
|
30
|
+
> events/bookings per-event registration form (a different thing). The Forms app's `appDefId` is
|
|
31
|
+
> `225dd912-7dea-4738-8688-4b8c6955ffc2`; an `UNSUPPORTED_FORM_NAMESPACE` error means the app isn't
|
|
32
|
+
> installed. Call the **public** host shown above (no `/_api/` prefix).
|
|
33
|
+
>
|
|
34
|
+
> **API reference:**
|
|
35
|
+
> - Create Form: <https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/create-form> — its
|
|
36
|
+
> examples are complete requests for common form types (contact, survey, order, booking, …). Copy
|
|
37
|
+
> the one closest to what you need.
|
|
38
|
+
> - About Form Fields:
|
|
39
|
+
> <https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/about-form-fields>
|
|
40
|
+
> - Form object: <https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/form-object>
|
|
15
41
|
|
|
16
42
|
---
|
|
17
43
|
|
|
18
44
|
## Article: Steps for Setting Up Wix Forms
|
|
19
|
-
**YOU MUST** complete the steps in order, without requiring additional user input: clean the default form (STEP 1) **before** creating yours (STEP 2), then verify (STEP 3).
|
|
20
45
|
|
|
21
|
-
**⚠️ CRITICAL ORDER REQUIREMENT: clean the install's default sample form FIRST (STEP 1), before
|
|
46
|
+
**⚠️ CRITICAL ORDER REQUIREMENT: clean the install's default sample form FIRST (STEP 1), before
|
|
47
|
+
creating any form.** Listing-then-deleting before you create guarantees every id you delete is a
|
|
48
|
+
pre-existing form, never one you just created — and it keeps you clear of the site's low form cap.
|
|
49
|
+
**But only delete forms that are obviously the install's own default sample form:** the site may
|
|
50
|
+
already hold the owner's **real forms** (a connect/iterate run, or an owner-populated site). If
|
|
51
|
+
what's there isn't obviously install sample data, or you're unsure, **do not delete it — ask the
|
|
52
|
+
user first** (`SEED.md`: seeding is additive; deleting real content needs the owner's approval).
|
|
22
53
|
|
|
23
54
|
### STEP 1: Clean — remove any pre-existing (install-default) forms
|
|
24
55
|
|
|
25
|
-
A freshly installed Wix Forms app **may ship a default "Get in touch" form** (a contact form with
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
STEP
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"steps": [
|
|
67
|
-
{ "id": "$STEP", "name": "Page 1", "layout": { "large": { "items": [
|
|
68
|
-
{ "fieldId": "$F1", "row": 0, "column": 0, "width": 12, "height": 1 },
|
|
69
|
-
{ "fieldId": "$F2", "row": 1, "column": 0, "width": 12, "height": 1 },
|
|
70
|
-
{ "fieldId": "$F3", "row": 2, "column": 0, "width": 12, "height": 1 },
|
|
71
|
-
{ "fieldId": "$SUBMIT", "row": 3, "column": 0, "width": 12, "height": 1 }
|
|
72
|
-
], "sections": [] } } }
|
|
73
|
-
],
|
|
74
|
-
"enabled": true
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**⚠️ CRITICAL FORMAT REQUIREMENTS:**
|
|
80
|
-
- **`namespace` MUST be `"wix.form_app.form"`** (the Wix Forms namespace) — any other value fails with `400 UNSUPPORTED_FORM_NAMESPACE`. It is immutable after create.
|
|
81
|
-
- **Every INPUT field MUST carry a non-empty `target`** — the human-readable key the frontend binds to (input `name` = `target`). An empty/missing target fails with `400 UNSUPPORTED_FIELD_TARGETS_NAME` (`MISSING_FIELD_TARGETS`). Targets are **immutable** (set once).
|
|
82
|
-
- **Targets MUST be unique within a form** — two fields sharing a target → `400 DUPLICATED_FIELD_TARGETS`. Use lowercase snake_case keys the frontend can reuse verbatim (`first_name`, `email`, `phone`, `message`).
|
|
83
|
-
- **Field envelope:** each field is `{ id, hidden: false, identifier: "<CONTACTS_*>", fieldType: "INPUT", inputOptions: { target, readOnly: false, inputType: "STRING", stringOptions: { validation: { format, enum: [] }, componentType: "TEXT_INPUT", textInputOptions: { label, showLabel } } } }`. The `identifier` is MANDATORY for dashboard rendering — see the CRITICAL identifier block below. Use `inputType: "STRING"` + `componentType: "TEXT_INPUT"` for text fields. Set `required: true` on the fields the form must collect (defaults to `false`); mark `pii: true` on personal fields (name/email/phone).
|
|
84
|
-
|
|
85
|
-
**Dropdown (enum) fields — concrete envelope.** A dropdown is still `inputType: "STRING"`; only the `componentType` and its options block change. Set `componentType: "DROPDOWN"` and put the choices under `stringOptions.dropdownOptions.options` as `{ id, label, value }` — the `value` is what a submission stores (keep it a stable machine key), and **every option needs its own lowercase GUID `id`** (see the CRITICAL below). Keep the `validation` block as with any field (a plain dropdown reads back `format: UNDEFINED`, so write `UNKNOWN_FORMAT`):
|
|
56
|
+
A freshly installed Wix Forms app **may ship a default "Get in touch" form** (a contact form with
|
|
57
|
+
`first_name` / `email` / `message` fields). Its presence is **not deterministic** — some fresh
|
|
58
|
+
installs ship it and others don't, so it appears provisioning/timing-dependent. Rather than assume,
|
|
59
|
+
**list what's actually there and delete whatever comes back** — this is a safe no-op when the list
|
|
60
|
+
is empty.
|
|
61
|
+
|
|
62
|
+
1. **List the existing forms** —
|
|
63
|
+
`GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&fieldsets=METADATA`,
|
|
64
|
+
then again with `&enabled=false`. Collect every `form.id` from both responses (`forms[].id`).
|
|
65
|
+
2. **Delete each** — `DELETE https://www.wixapis.com/form-schema-service/v4/forms/{formId}` (one
|
|
66
|
+
call per id; returns `200 {}`). Because the list ran **before** any create, every id returned is
|
|
67
|
+
a pre-existing form — safe to delete. If the list was empty, issue no DELETE (a correct no-op).
|
|
68
|
+
|
|
69
|
+
**⚠️ Why clean even though it's often a no-op — the form cap.** Leftovers plus your new forms can
|
|
70
|
+
exhaust the site's form allowance (read it from
|
|
71
|
+
[List Forms Providers Configs](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/list-forms-providers-configs)),
|
|
72
|
+
which is also why you must **never create throwaway forms to probe the shape.**
|
|
73
|
+
|
|
74
|
+
### STEP 2: Create each form schema
|
|
75
|
+
|
|
76
|
+
**One POST per form** to `https://www.wixapis.com/form-schema-service/v4/forms`. How many forms, and
|
|
77
|
+
each form's fields and labels, come from the request you're fulfilling; this step gives the call and
|
|
78
|
+
the required format. Forms are independent (no shared revision), so concurrent creates are safe.
|
|
79
|
+
|
|
80
|
+
**⚠️ Generate every `id` in the shell as a lowercase UUID v4 — never type one from memory**
|
|
81
|
+
(`uuidgen | tr 'A-Z' 'a-z'`). Supply them at create rather than omitting them: `steps` must
|
|
82
|
+
reference each field by `fieldId` in the same request.
|
|
83
|
+
|
|
84
|
+
**Assemble the request** from the closest Create Form example, building each `formFields[]` entry —
|
|
85
|
+
including the `SUBMIT_BUTTON` — per
|
|
86
|
+
**[About Form Fields](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/about-form-fields)**,
|
|
87
|
+
which owns every field-level rule, including placing every field in the layout — **with one
|
|
88
|
+
documented gap: where `required` goes (next paragraph). The guide never says, so don't infer it from
|
|
89
|
+
its "Validation" section.**
|
|
90
|
+
**Geometry (`row`/`column`/`width`) does not matter on headless** — the frontend renders its own
|
|
91
|
+
layout from `formFields[]` and never reads `steps[].layout` (`how-to-code-forms.md`) — so the layout
|
|
92
|
+
is a *correctness* requirement, not a design one: assert presence and coverage only. Use a single
|
|
93
|
+
step unless the request needs multiple pages.
|
|
94
|
+
|
|
95
|
+
**⚠️ `required` goes at `inputOptions.required` — NOT inside the field's `validation` block.** It sits
|
|
96
|
+
beside `target`/`inputType`:
|
|
86
97
|
|
|
87
98
|
```jsonc
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"target": "budget", "required": false, "inputType": "STRING", "readOnly": false,
|
|
92
|
-
"stringOptions": {
|
|
93
|
-
"validation": { "format": "UNKNOWN_FORMAT", "enum": [] },
|
|
94
|
-
"componentType": "DROPDOWN",
|
|
95
|
-
"dropdownOptions": {
|
|
96
|
-
"label": "Budget", "showLabel": true, "placeholder": "Select a range",
|
|
97
|
-
"options": [
|
|
98
|
-
{ "id": "$O1", "label": "Under $1k", "value": "under_1k" },
|
|
99
|
-
{ "id": "$O2", "label": "$1k–$5k", "value": "1k_5k" },
|
|
100
|
-
{ "id": "$O3", "label": "$5k+", "value": "5k_plus" }
|
|
101
|
-
]
|
|
102
|
-
} } } }
|
|
99
|
+
"inputOptions": { "target": "full_name", "required": true, "inputType": "STRING",
|
|
100
|
+
"stringOptions": { "validation": {}, "componentType": "TEXT_INPUT",
|
|
101
|
+
"textInputOptions": { "label": "Full Name", "showLabel": true } } }
|
|
103
102
|
```
|
|
104
103
|
|
|
105
|
-
|
|
104
|
+
A `required` key inside `stringOptions.validation` — or any `<inputType>Options.validation`, since no
|
|
105
|
+
input type's validation object defines it — is **accepted at create and then silently discarded**:
|
|
106
|
+
`200`, the form lists, the summary returns every field, and every field still reads back
|
|
107
|
+
`required: false`. The form ships publicly with nothing mandatory and **no error anywhere in the
|
|
108
|
+
response**, so STEP 3's read-back diff is the only signal. `validation` carries value constraints
|
|
109
|
+
only (`format`, `enum`, `minimum`, `minLength`, `items`). The lone exception is the multi-line address
|
|
110
|
+
field, whose per-subfield flags genuinely live at
|
|
111
|
+
`addressOptions.validation.fields.<sub>.required`.
|
|
112
|
+
|
|
113
|
+
**⚠️ Any example value you author into the schema follows the site's country, not your default.** A
|
|
114
|
+
`placeholder` (or label/hint) carrying a phone number, postcode, currency or date is **visible to
|
|
115
|
+
every visitor**, so a `+44`- or US-shaped example on a site in another market is a locale bug. Resolve
|
|
116
|
+
the site's country and, for an illustrative phone number, take it from that country's
|
|
117
|
+
regulator-reserved fictional range — the resolution order and the vetted per-country map live in
|
|
118
|
+
`how-to-code-forms.md` ("Validation"). Leaving a constrained field's `placeholder` empty is fine: the
|
|
119
|
+
frontend synthesizes the example from the field's `format` at render time.
|
|
120
|
+
|
|
121
|
+
**⚠️ A `200` proves nothing: always run STEP 3.** Most mistakes here are accepted at create and only
|
|
122
|
+
surface in the dashboard or on the first real submission.
|
|
123
|
+
|
|
124
|
+
#### Plan gates — surface the choice, never engineer around it
|
|
125
|
+
|
|
126
|
+
Three plan-tier limits return a real `400` on create. **Do not work around any of them; tell the user
|
|
127
|
+
to reduce or upgrade.**
|
|
128
|
+
|
|
129
|
+
- **`FIELDS_COUNT_RESTRICTIONS_ERROR`** — the form exceeds the plan's per-form field cap. **Do NOT
|
|
130
|
+
split the form into multiple schemas** to dodge it. Reduce the field count, or upgrade.
|
|
131
|
+
- **`FILE_UPLOAD_RESTRICTIONS_ERROR`** — a file upload, signature, or payment field on a plan below
|
|
132
|
+
Core. **Do NOT suggest inlining files as base64** — it stores no real file, gives the owner nothing
|
|
133
|
+
usable, and blows past submission size limits. Drop the field, or upgrade.
|
|
134
|
+
- **`FORMS_COUNT_RESTRICTIONS_ERROR`** — the site hit its plan's total-form cap. Upgrade, or free a
|
|
135
|
+
slot (STEP 1's list-then-delete — but only delete forms that are clearly install sample data; ask
|
|
136
|
+
before deleting anything that could be the owner's real form).
|
|
137
|
+
|
|
138
|
+
**⚠️ A plan gate is a HARD BLOCK on the run — not a soft "record it and continue" precondition like
|
|
139
|
+
paid tickets or online reservations.** Those leave a working schema and fail only at runtime; here the
|
|
140
|
+
schema doesn't exist, so its `formId`/field `target`s don't either. **Put the choice to the user
|
|
141
|
+
(reduce, or upgrade — with the MSID + dashboard link), wait for their confirmation, then create and
|
|
142
|
+
verify the schema (STEP 2 → STEP 3) BEFORE any frontend work.** Do **not** build the frontend "in the
|
|
143
|
+
meantime": its inputs bind to those targets, so every binding would be a guess to rewrite.
|
|
144
|
+
|
|
145
|
+
Read `form.id` from the response as the `formId` to keep.
|
|
146
|
+
|
|
147
|
+
If a create fails transiently on a fresh site (`5xx`, or an identity/propagation error right after
|
|
148
|
+
install — the install returns `appInstance.status: "UNKNOWN"` until it propagates), retry the same
|
|
149
|
+
call **once**; do not loop.
|
|
106
150
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
**⚠️ A custom dropdown's options round-trip in `fields[].view.options`, NOT `formFields[]` — and the frontend reads them from there.** Verified live (2026-07-12): a dropdown whose `identifier` is a plain custom key (like `"budget"` — dropdowns rarely map to a `CONTACTS_*` system field) is **dropped from `formFields[]` by the identifier→dashboard rule below**, so it does **not** show in the Wix dashboard summary — *but its data is fully captured and its options survive under `form.fields[].view.options` as `[{ id, label, value }]`* (alongside `view.label` / `view.placeholder`). That's exactly where the schema-driven frontend reads dropdown choices from (`how-to-code-forms.md` projects `fields[]`, not `formFields[]`), so **a custom dropdown renders and submits correctly on the headless site even though the dashboard doesn't list it.** So: seed the dropdown as the request needs; verify persistence on the GET round-trip (STEP 3) by asserting the field appears in `fields[]` with a non-empty `view.options`; and — as with any custom (non-`CONTACTS_*`) field — do **not** claim the dashboard fully renders it (platform limitation, same as the CRITICAL identifier block). If a dropdown *does* map cleanly to a contact field, give it the matching `CONTACTS_*` identifier and it will also show in the dashboard.
|
|
110
|
-
|
|
111
|
-
Other richer types (number, checkbox group, date) follow the same envelope with a different `inputType`/`stringOptions`/`arrayOptions` — but plain STRING text + dropdowns cover the lead-capture case.
|
|
112
|
-
|
|
113
|
-
**⚠️ CRITICAL: every field MUST carry a `stringOptions.validation` block, or the field is NOT submittable — a visitor submission of it fails with `400 "must NOT have additional properties"`.** The submission validator builds its allowed-keys schema **only from fields that have a `validation` block**. A field created without one exists on the form and even renders, but any submission that includes its `target` is rejected as an unknown property (and `required` is silently dropped too) — the form looks fine but silently rejects real submissions. So give **every** field a `validation` block:
|
|
114
|
-
- Plain text / name / message → `"validation": { "format": "UNKNOWN_FORMAT", "enum": [] }`.
|
|
115
|
-
- Email → `"validation": { "format": "EMAIL", "enum": [] }`; phone → `"PHONE"`; URL → `"URL"`. (Full `format` enum: `UNKNOWN_FORMAT`, `DATE`, `TIME`, `DATE_TIME`, `EMAIL`, `URL`, `UUID`, `PHONE`, `URI`, `HOSTNAME`, `COLOR_HEX`, `CURRENCY`, `LANGUAGE`, `DATE_OPTIONAL_TIME`.)
|
|
116
|
-
- **Use `UNKNOWN_FORMAT`, NOT `UNDEFINED`, on write.** The create/GET response echoes an unconstrained format back as `"UNDEFINED"` (and the docs' create example shows `"format": "UNDEFINED"`), but sending `"UNDEFINED"` on **create** fails with `400 "format enum must be in […]"` — `UNDEFINED` is the read-back value, not a writable one. Write `UNKNOWN_FORMAT`; it stores and reads back as `UNDEFINED`.
|
|
117
|
-
- **Length & pattern constraints go in the SAME `validation` block — seed them when the request asks for field rules.** `stringOptions.validation` also accepts `minLength` (int), `maxLength` (int), and `pattern` (a regex string) alongside `format`/`enum`. A request like "first name 2–40 chars", "message at least 20 chars", or "note capped at 500" maps directly to these. Verified live (2026-07-12): `"validation": { "format": "UNKNOWN_FORMAT", "enum": [], "minLength": 2, "maxLength": 12, "pattern": "^[A-Za-z ]+$" }` persists and reads back under `validation.string.{minLength,maxLength,pattern}`. **These constraints are enforced server-side by `createSubmission`** — a violating submit is rejected with a field violation (`errorPath` = the field's `target`, e.g. message `"must have between 2 and 12 characters"`), which the frontend surfaces inline (see `how-to-code-forms.md`). The frontend also reads them from the schema to pre-validate client-side, so seeding them is what makes owner-set field rules flow through with no code change. `pattern` is a standard JS-style regex; omit it unless the request calls for a specific character rule.
|
|
118
|
-
- `required` (at `inputOptions` level) lands in `validation.required` — but **only takes effect if the field has a `validation` block** (no block ⇒ `required` is dropped along with the field's submittability). This is another reason every field needs the block.
|
|
119
|
-
- If a create fails transiently on a fresh site (`5xx`, or an identity/propagation error right after install — the install returns `appInstance.status: "UNKNOWN"` until it propagates), retry the same call **once**; do not loop. (Creates typically succeed immediately, but keep the retry-once safety net.)
|
|
120
|
-
|
|
121
|
-
**⚠️ CRITICAL: include a `steps` layout + a `SUBMIT_BUTTON` DISPLAY field, or the Wix dashboard shows every submission EMPTY.** The form is technically submittable with INPUT fields alone — the submission *data* stores fine either way, and the headless frontend renders its own UI — BUT the Wix **Forms dashboard** (`Customers & Leads ▸ Forms & Submissions`) renders each submission's values against the form's **layout** (`formFields` + `steps`). A form seeded **without** a layout stores complete submissions that the dashboard displays as **blank** ("—" summary, empty details panel), so the site owner can't read their leads in the UI — it looks broken even though the data is intact. So always seed the layout:
|
|
122
|
-
- Add a **`SUBMIT_BUTTON`** DISPLAY field (`fieldType: "DISPLAY"`, `displayOptions.displayFieldType: "PAGE_NAVIGATION"`) to `formFields`.
|
|
123
|
-
- Add a **single `steps` entry** whose `layout.large.items[]` places **every** field (each INPUT + the submit button) by `fieldId`, one per row.
|
|
124
|
-
- **Each `items[].fieldId` MUST equal the corresponding `formFields[].id`** — this is why the ids are generated **lowercase** up front (the server lowercases stored field ids; an uppercased layout `fieldId` would no longer match and the dashboard layout silently breaks).
|
|
125
|
-
- A **single create call with the layout persists it** — no follow-up PATCH needed. (Omitting the layout is the cause of blank dashboard submissions; adding it via PATCH to an already-created form also fixes them retroactively.)
|
|
126
|
-
|
|
127
|
-
**⚠️⚠️ CRITICAL: every INPUT field MUST carry a system `identifier`, or it is DROPPED FROM `formFields[]` and NEVER shows in the dashboard.** This is the #1 dashboard-blank cause and it is SEPARATE from (and stronger than) the layout requirement above — a form can have a perfect `steps` layout and still be blank. On create, the server keeps an INPUT field's component in `formFields[]` **only if its `identifier` is a recognized system value.** Without a recognized identifier (no identifier, a custom string, an extended-field key like `custom.x`, or a GUID — all fail) the field is normalized into the legacy `fields[]` array **only**, `formFields[]` comes back holding just the `SUBMIT_BUTTON`, and `GET .../{formId}/summary` returns **zero fields** → the Wix dashboard renders the form and every submission **blank**. (The public headless site still submits fine — the submission service matches by `target` — so this defect is invisible from the frontend; you must verify it server-side in STEP 3.)
|
|
128
|
-
- **Map each field to its contact identifier.** Set `identifier` on every INPUT field to the `CONTACTS_*` value for what it collects. Verified working: `CONTACTS_FIRST_NAME`, `CONTACTS_LAST_NAME`, `CONTACTS_EMAIL`, `CONTACTS_PHONE`. (The contacts schema has more system fields — address, company, birthdate, etc.; use the matching `CONTACTS_*` identifier. `SUBMIT_BUTTON` is the identifier for the DISPLAY submit field.)
|
|
129
|
-
- **⚠️ Custom (non-contact) fields do NOT render in the dashboard via this API.** A field with no matching `CONTACTS_*` identifier (e.g. "medical conditions", "fitness goal", a waiver checkbox) cannot be made to appear in the Wix Forms dashboard through `form-schema-service/v4/forms` — registering a Contacts extended field and referencing its `custom.<key>` does **not** work. Such fields still store submission data correctly (frontend + `fields[]` + submission records) but the owner won't see them in the dashboard UI. **So: for lead-capture forms the request should lean on contact-mappable fields (name / email / phone / address / company / birthdate). If the request genuinely needs custom fields, seed them anyway (data is captured) but do NOT report the dashboard as fully rendering — only the contact-mapped fields will show.** This is a platform limitation, not a seed bug; surface it rather than silently shipping a half-blank dashboard.
|
|
130
|
-
- Also set `hidden: false` on each field and `readOnly: false` in `inputOptions` (matches the shape shown in the docs).
|
|
131
|
-
|
|
132
|
-
**⚠️ Do NOT rely on `postSubmissionTriggers.upsertContact` (contact mapping) through this endpoint — it is SILENTLY DROPPED.** The docs' create example includes a `postSubmissionTriggers.upsertContact.fieldsMapping` block to auto-create a CRM contact from a submission. Live, the create returns `200` **but the trigger is not persisted** (absent from a follow-up GET — the same silent-drop pattern as CMS multi-refs at insert). Don't depend on it in the seed. You don't need it: **form submissions are recorded against the schema regardless**, so leads are captured by the seeded form's `formId` on their own. The seed's job ends at the schema + targets.
|
|
151
|
+
### STEP 3: Verify each form persisted (mandatory)
|
|
133
152
|
|
|
134
|
-
|
|
153
|
+
A `200` on create is not proof the form is queryable or that the dashboard will render it.
|
|
154
|
+
|
|
155
|
+
1. **List once** —
|
|
156
|
+
`GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&formIds=<id1>&formIds=<id2>`
|
|
157
|
+
— **`formIds` narrows the list to exactly the forms you just created**, so you assert against them
|
|
158
|
+
directly instead of filtering a whole-namespace listing. For each form, confirm its `id` appears,
|
|
159
|
+
its **`formFields[]`** covers every field you sent, and its **`steps` is non-empty and places
|
|
160
|
+
every field** (per About Form Fields). **Also diff every field's `inputOptions.required` against
|
|
161
|
+
what you sent** — a misplaced `required` is dropped silently (STEP 2), and this read-back is the
|
|
162
|
+
only signal. If a flag came back `false`, fix it with STEP 4's `PATCH` (moving the flag to
|
|
163
|
+
`inputOptions.required`) rather than deleting and re-creating: the `formId` survives and it costs
|
|
164
|
+
no extra slot against the form cap.
|
|
165
|
+
2. **⚠️ Then verify the dashboard will actually render —
|
|
166
|
+
`GET https://www.wixapis.com/form-schema-service/v4/forms/{formId}/summary` and assert
|
|
167
|
+
`formSummary.fields` is NON-EMPTY**, with a count equal to **every input field you sent** (i.e.
|
|
168
|
+
`formFields[]` minus the `SUBMIT_BUTTON`). A 6-input form returns all 6 — **including non-contact
|
|
169
|
+
`DROPDOWN` and `TEXT_AREA` fields** — so do *not* expect only the contact-mapped ones. This is
|
|
170
|
+
the reliable dashboard-truth check: the summary returns exactly the fields the Wix dashboard
|
|
171
|
+
shows. A `summary.fields: []`, or a count short of your inputs, means the form renders blank (or
|
|
172
|
+
partly blank) for the owner even though the public site submits fine — **do not report success;
|
|
173
|
+
fix the `identifier`s and re-create.**
|
|
174
|
+
|
|
175
|
+
3. **⚠️ If the form has a multi-choice ARRAY field (`CHECKBOX_GROUP` / `TAGS`), the two checks above
|
|
176
|
+
are NOT enough — send one real `createSubmission`.** A malformed `arrayOptions.validation.items`
|
|
177
|
+
(missing `itemType`, or an empty/omitted `items`) still lists fine and still counts in the
|
|
178
|
+
summary, so steps 1–2 pass while **every** submission to the form `400`s form-wide (see "Choice
|
|
179
|
+
fields"). The only proof is a live submission:
|
|
180
|
+
`POST https://www.wixapis.com/form-submission-service/v4/submissions` with the standard call
|
|
181
|
+
shape (`<AUTH>`, `Content-Type`, `wix-site-id`) and a minimal valid body — `formId` plus a
|
|
182
|
+
`submissions` map keyed by each field's `target`, the ARRAY field as an **array** of enum values:
|
|
183
|
+
|
|
184
|
+
```jsonc
|
|
185
|
+
{ "submission": { "formId": "<formId>",
|
|
186
|
+
"submissions": { "email": "test@example.com", "multi_choice": ["Option 1"] } } }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Assert it returns `200`, not `400 SUBMISSION_VALIDATION`. A `400` here means the ARRAY `items`
|
|
190
|
+
shape is wrong — fix it (both `itemType` and `stringOptions.enum`) and re-create. Delete the test
|
|
191
|
+
submission afterward
|
|
192
|
+
(`DELETE https://www.wixapis.com/form-submission-service/v4/submissions/{submissionId}`, the `_id`
|
|
193
|
+
from the response) so the owner's dashboard stays clean.
|
|
194
|
+
|
|
195
|
+
If a form is missing, its layout didn't persist, or its summary is unexpectedly empty, re-create it
|
|
196
|
+
once and re-verify; if it still fails, surface the response verbatim rather than reporting success.
|
|
197
|
+
|
|
198
|
+
### STEP 4 (when revising): update an existing form
|
|
199
|
+
|
|
200
|
+
To change a form the request has since revised — add a field, relabel one, tighten a rule — follow
|
|
201
|
+
[Update Form](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/update-form):
|
|
135
202
|
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
"id": "<formId>",
|
|
139
|
-
"fields": [
|
|
140
|
-
{ "id": "<field-id>", "target": "first_name", "view": { "label": "First name" }, "pii": true },
|
|
141
|
-
{ "id": "<field-id>", "target": "email", "validation": { "string": { "format": "EMAIL" }, "required": true } }
|
|
142
|
-
],
|
|
143
|
-
"formFields": [ /* the field components incl. the submit button */ ],
|
|
144
|
-
"steps": [ /* the layout you sent */ ],
|
|
145
|
-
"namespace": "wix.form_app.form", "name": "Get in touch", "enabled": true
|
|
146
|
-
} }
|
|
203
|
+
```
|
|
204
|
+
PATCH https://www.wixapis.com/form-schema-service/v4/forms/{formId}
|
|
147
205
|
```
|
|
148
206
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
A `200` on create is not proof the form is queryable. After creating, **list once** and confirm every seeded form is present with its targets:
|
|
207
|
+
**Prefer updating over delete-and-recreate.** An update keeps the `formId` the handoff already
|
|
208
|
+
carries and consumes no additional slot against the site's form cap. **Re-run STEP 3 after any update**
|
|
209
|
+
— it can regress the layout or the dashboard summary exactly as a create can.
|
|
154
210
|
|
|
155
|
-
|
|
211
|
+
---
|
|
156
212
|
|
|
157
|
-
|
|
213
|
+
## Keep — what crosses into the handoff
|
|
158
214
|
|
|
159
|
-
|
|
215
|
+
**Per form: the `formId` + each form's field `target` keys.**
|
|
160
216
|
|
|
161
|
-
|
|
217
|
+
The `target`s are *structural* — the frontend binds each input's `name` to a field's `target` to
|
|
218
|
+
submit (the same carve-out shape as cms's `collectionId` + field keys). Everything else — the field
|
|
219
|
+
set, order, labels, `required` flags, validation formats and dropdown options — is **read live from
|
|
220
|
+
the schema at render time** (visitor token, no `auth.elevate` — `how-to-code-forms.md`), so a field
|
|
221
|
+
the owner adds, removes or relabels reflects on the site with no code change. See
|
|
222
|
+
`SDK_HANDOFF.md` §4.
|
|
162
223
|
|
|
163
224
|
---
|
|
164
225
|
|
|
165
226
|
## Conclusion
|
|
227
|
+
|
|
166
228
|
Following these steps **in order** sets up a Wix Forms backend:
|
|
167
|
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
229
|
+
|
|
230
|
+
- Starts from a **clean form list** — pre-existing forms are listed-then-deleted first (a safe no-op
|
|
231
|
+
when none exist), keeping clear of the site's form cap.
|
|
232
|
+
- Contains exactly the forms the request calls for, created on **Form Schemas v4** in the
|
|
233
|
+
**`wix.form_app.form`** namespace, each field built per **About Form Fields**, with lowercase GUID
|
|
234
|
+
ids and a `steps` layout placing every field plus a `SUBMIT_BUTTON`.
|
|
235
|
+
- Is **verified via `GET .../forms/{formId}/summary`** (non-empty, count equal to every input field
|
|
236
|
+
— contact-mapped or not) — not merely by a `200` on create, and not merely by `steps` being
|
|
237
|
+
present.
|
|
238
|
+
- Revises forms with **`PATCH` + the current `revision`** rather than delete-and-recreate, keeping
|
|
239
|
+
the `formId` and the form slot.
|
|
240
|
+
- **Keeps** per form the **`formId` + field `target`s** (the immutable submission keys) — the
|
|
241
|
+
frontend reads everything else (labels, options, order) live from the schema.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wix-manage
|
|
3
|
-
description: "Wix business solution management recipes — REST API operations for configuring and managing Wix business solutions. Routes to: stores, bookings, get-paid, CMS, contacts, forms, media, app-installation, pricing-plans, restaurants, rich-content, sites, blog, calendar, domains, events, site-properties, ecommerce, marketing, google-ads, analytics, dashboard-navigation."
|
|
3
|
+
description: "Wix business solution management recipes — REST API operations for configuring and managing Wix business solutions. Routes to: stores, bookings, get-paid, CMS, contacts, forms, media, app-installation, pricing-plans, restaurants, rich-content, sites, blog, calendar, domains, events, site-properties, ecommerce, marketing, google-ads, analytics, accessibility, dashboard-navigation."
|
|
4
4
|
compatibility: Requires Wix REST API access (API key or OAuth).
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -34,6 +34,13 @@ These recipes do NOT cover frontend development or SDK usage for displaying data
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
+
## Accessibility
|
|
38
|
+
|
|
39
|
+
### [Scan a Wix Site for Accessibility Issues](references/accessibility/scan-site-accessibility.md)
|
|
40
|
+
**Technical:** Runs an asynchronous accessibility scan for a full site, one page, or every page in any supported Wix page collection. Discovers collection IDs instead of hardcoding verticals, polls the scan to completion, separates failed pages from clean pages, and returns prioritized, agent-friendly findings and fix guidance.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
37
44
|
## Analytics
|
|
38
45
|
|
|
39
46
|
### [Query Site Analytics](references/analytics/query-site-analytics.md)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Scan a Wix Site for Accessibility Issues"
|
|
3
|
+
description: Run a Wix accessibility scan for a full site, one page, or every page in any supported page collection, including products, blog posts, booking services, events, and restaurant pages. Poll the asynchronous scan to completion, report failed pages separately, retrieve prioritized findings, and use the returned fix guidance to help the user resolve and verify issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Scan a Wix Site for Accessibility Issues
|
|
7
|
+
|
|
8
|
+
Use the public **Accessibility Scans API** to scan the authenticated Wix site and
|
|
9
|
+
turn the results into an actionable accessibility report. The API selects the
|
|
10
|
+
site from the caller's authorization context; never ask for or send a site ID.
|
|
11
|
+
|
|
12
|
+
Starting a scan requires the **Manage Accessibility Scans** permission. Reading
|
|
13
|
+
collections or previous results does not prove that the caller can start new
|
|
14
|
+
work.
|
|
15
|
+
|
|
16
|
+
## Choose the target
|
|
17
|
+
|
|
18
|
+
Match the user's words to one target:
|
|
19
|
+
|
|
20
|
+
| User intent | Target |
|
|
21
|
+
|---|---|
|
|
22
|
+
| "Scan my site" | Full-site scope |
|
|
23
|
+
| "Scan this page" or a specific URL/page | One page, identified by its URL or page ID |
|
|
24
|
+
| "Scan all products/blog posts/menu pages/etc." | One supported page collection |
|
|
25
|
+
|
|
26
|
+
For a generated vertical page, prefer its page URL unless a unique Wix page ID
|
|
27
|
+
is already available. The caller never needs to know the page type.
|
|
28
|
+
|
|
29
|
+
For a collection request, first call **List Accessibility Scan Page
|
|
30
|
+
Collections** and match the user's intent to a returned display name. Pass its
|
|
31
|
+
`collectionId` unchanged to the scan. Never hardcode a list of verticals or
|
|
32
|
+
invent a collection ID: installed apps determine what the site currently
|
|
33
|
+
supports.
|
|
34
|
+
|
|
35
|
+
If the target is ambiguous, ask one short question before starting. An explicit
|
|
36
|
+
request to scan, check, or audit is already confirmation to create the scan job.
|
|
37
|
+
|
|
38
|
+
## Run and read the scan
|
|
39
|
+
|
|
40
|
+
1. Generate one UUID for this intended scan.
|
|
41
|
+
2. Call **Run Accessibility Scan** once and keep the returned scan ID. Reuse the
|
|
42
|
+
UUID only when retrying this exact request; use a new UUID for a new scan.
|
|
43
|
+
3. Poll **Get Accessibility Scan** with that scan ID. Respect each response's
|
|
44
|
+
suggested polling interval until the status is terminal.
|
|
45
|
+
4. On `COMPLETED` or `PARTIALLY_COMPLETED`, read:
|
|
46
|
+
- the scan summary for totals and executed-rule coverage;
|
|
47
|
+
- **List Accessibility Scan Page Summaries** for every discovered page,
|
|
48
|
+
including clear pages and pages that failed to scan;
|
|
49
|
+
- **List Accessibility Scan Findings** for actionable issues. Follow cursor
|
|
50
|
+
paging for the result set the user requested.
|
|
51
|
+
5. On `FAILED`, report the public failure guidance. Do not request findings or
|
|
52
|
+
present the scan as clean.
|
|
53
|
+
|
|
54
|
+
Use a findings filter when the user asks about one page, rule, severity, or
|
|
55
|
+
accessibility category. Do not issue a separate findings request for every page.
|
|
56
|
+
|
|
57
|
+
## Present actionable results
|
|
58
|
+
|
|
59
|
+
Lead with the scan status and aggregate totals. Then:
|
|
60
|
+
|
|
61
|
+
- Report failed pages separately. A failed page with zero findings is
|
|
62
|
+
**unknown**, not clean.
|
|
63
|
+
- Prioritize findings by severity, then page and rule.
|
|
64
|
+
- For each useful finding, include the affected page and element reference,
|
|
65
|
+
what failed, why it matters, WCAG criteria, remediation summary, and the
|
|
66
|
+
ordered fix and verification steps.
|
|
67
|
+
- Use the returned collection and item IDs to identify the owning business
|
|
68
|
+
entity when a generated page must be updated through another public Wix API.
|
|
69
|
+
- If `humanInputRequired` is true, explain the decision needed and ask the user
|
|
70
|
+
instead of guessing content, intent, or design.
|
|
71
|
+
|
|
72
|
+
The scan API reports issues but does not edit the site. Get confirmation before
|
|
73
|
+
applying fixes through another API or editor capability. After fixes, run the
|
|
74
|
+
same target again with a **new** idempotency key and compare the results.
|
|
75
|
+
|
|
76
|
+
## Recovery rules
|
|
77
|
+
|
|
78
|
+
- **Permission denied when starting a scan:** stop after the first `403` or
|
|
79
|
+
`PERMISSION_DENIED`. Do not retry with another URL, request shape, target, or
|
|
80
|
+
site. Explain that the current Wix identity cannot start accessibility scans
|
|
81
|
+
and must be reconnected or authorized with **Manage Accessibility Scans**.
|
|
82
|
+
Never imply that collection discovery means the scan ran.
|
|
83
|
+
- **Scan already in progress:** poll the existing scan ID returned by the error;
|
|
84
|
+
do not start a duplicate.
|
|
85
|
+
- **Scan state unknown:** read the returned scan ID until its state is known;
|
|
86
|
+
do not start another scan.
|
|
87
|
+
- **Rate limited or transient retry:** wait, then retry the same intended run
|
|
88
|
+
with the same idempotency key.
|
|
89
|
+
- **Target unavailable:** ask for a valid page or refresh the collection list.
|
|
90
|
+
- **Scan size limit exceeded:** never imply the site was fully scanned. Narrow
|
|
91
|
+
the target, preferably into supported page collections, and report the limit.
|
|
92
|
+
- **Partially completed:** return the usable findings and clearly list every
|
|
93
|
+
failed page and its failure guidance.
|
|
94
|
+
- **Results expired:** start a new scan only when the user still wants fresh
|
|
95
|
+
results.
|
|
96
|
+
|
|
97
|
+
Load the current public API reference before constructing requests so field
|
|
98
|
+
names, filters, permissions, and error schemas come from the live contract.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Analytics Dashboard Navigation"
|
|
3
|
-
description: "Builds direct links to Wix Analytics dashboard pages on manage.wix.com — highlights, reports,
|
|
3
|
+
description: "Builds direct links to Wix Analytics dashboard pages on manage.wix.com — highlights, reports, per-domain overviews (traffic, behavior, sales, marketing), and performance insights/benchmarks. Pairs analytics data with its read API so you can answer a question via API and hand back a 'see it in your dashboard' link. Use when the user asks where something is in the Wix dashboard, wants a direct link to a dashboard page, or you need a dashboard URL to include with the result of an API operation."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Analytics Dashboard Navigation
|
|
@@ -19,7 +19,6 @@ https://manage.wix.com/dashboard/{metaSiteId}/analytics/{route}
|
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| Highlights | `analytics/highlights` | Key metrics at a glance |
|
|
21
21
|
| Reports | `analytics/reports` | The full report library |
|
|
22
|
-
| Custom reports | `analytics/custom-reports` | User-built reports |
|
|
23
22
|
| Traffic overview | `analytics/overviews/traffic` | Sessions, sources, top pages |
|
|
24
23
|
| Behavior overview | `analytics/overviews/behavior` | On-site behavior |
|
|
25
24
|
| Sales overview | `analytics/overviews/sales` | Revenue and orders |
|