@wix/agent-skills 1.18.0 → 1.18.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Forms Dashboard Navigation"
3
- description: "Builds direct links to Wix Forms dashboard pages on manage.wix.com — the forms list, the submissions table, the form builder for a specific form, standalone forms, templates, and forms settings. Pairs each main Forms entity (form, submission) with its read API so you can fetch an entity and hand back a 'view 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."
3
+ description: "Builds direct links to Wix Forms dashboard pages on manage.wix.com. The paths are not guessable and appear in no API reference, so take them from here: under `https://manage.wix.com/dashboard/{metaSiteId}/`, the forms list is `wix-forms`, a form's builder is `wix-forms/form/{formId}`, and that form's submissions are `wix-forms/form/{formId}/submissions` — there is no site-wide submissions page, and nothing lives under `contacts/forms`, `forms`, `form-builder` or `wix-forms-and-payments` (the legacy app). Also standalone forms, and each Forms entity paired with its read API so you can fetch one and hand back a 'view it in your dashboard' link."
4
4
  ---
5
5
 
6
6
  # Forms Dashboard Navigation
@@ -9,36 +9,39 @@ Build direct links into the Wix Forms pages of a site's dashboard. For the gener
9
9
 
10
10
  ## Main Pages
11
11
 
12
+ **Never goess or answer a Forms dashboard-link request from memory** because the routes below are the only correct ones.
13
+
12
14
  | Page | URL after `/dashboard/{metaSiteId}/` | What it manages |
13
15
  |---|---|---|
14
16
  | Forms list | `wix-forms` | All forms on the site |
15
17
  | Form builder | `wix-forms/form/{formId}` | Edit a specific form (fields, rules, submit settings) |
18
+ | Submissions | `wix-forms/form/{formId}/submissions` | That form's submissions table |
16
19
  | Standalone form | `wix-forms/standalone-form` | Create a standalone form (has its own shareable page, not embedded in a site page) |
17
- | Submissions | `wix-forms-and-payments/submissions` | Submissions table across forms |
18
- | Templates | `wix-forms-and-payments/templates` | Create a form from a template |
19
- | Forms settings | `wix-forms-and-payments/settings` | Forms-level settings |
20
20
 
21
- Two URL namespaces appear here because the forms management pages and the form builder are registered by two apps (`14ce1214-b278-a7e4-1373-00cebd1bef7c` and `225dd912-7dea-4738-8688-4b8c6955ffc2`); `wix-forms` also redirects to the forms list, so it's the stable entry point.
21
+ **Every Forms page is under `wix-forms`** — the namespace **Wix Forms (New)** `225dd912-7dea-4738-8688-4b8c6955ffc2` registers, the app that owns the Form Schemas v4 API. A `wix-forms-and-payments/...` path belongs to the legacy **Wix Forms (Old)** app `14ce1214-b278-a7e4-1373-00cebd1bef7c`: don't build one, and never install or target that app (see [Create Form](./create-form.md) § Silent breakers).
22
+
23
+ **Submissions are per form**, hanging off that form's builder path — there is no site-wide submissions page to link to. With no `{formId}` in hand, link the forms list and let the owner pick.
22
24
 
23
- Entity-specific links (`{formId}`) accept the entity's ID appended as an extra path segment. Query params can follow.
25
+ Entity-specific links (`{formId}`) take the entity's ID as a path segment, and query params can follow. The dashboard appends its own view state to the submissions URL — `folder` (a JSON-encoded breadcrumb), `sort` (e.g. `createdDate+desc`) and `selectedColumns` (a comma-separated list of field **`target`**s) — none of which you need to construct: the bare path opens the table with the owner's defaults.
24
26
 
25
27
  ## Pairing Entities with Their Read APIs
26
28
 
27
- Fetch the entity via REST, then link the matching dashboard page. All calls use `https://www.wixapis.com` with an `Authorization` header. Dashboard forms use the `wix.form_app.form` namespace.
29
+ Fetch the entity via REST, then link the matching dashboard page. All calls use `https://www.wixapis.com` with an `Authorization` header. Dashboard forms live in the **`wix.form_app.form`** namespace — every Forms call that takes a namespace, as a query parameter or inside a filter, takes that exact value.
28
30
 
29
31
  | Entity | Read API | Dashboard link |
30
32
  |---|---|---|
31
33
  | Form | `GET /form-schema-service/v4/forms` · `POST /form-schema-service/v4/forms/query` · `GET /form-schema-service/v4/forms/{formId}` | `wix-forms/form/{formId}` (edit) or `wix-forms` (list) |
32
- | Submission | `POST /form-submission/v4/submissions/namespace/query` (filter must include the `namespace`) · `GET /form-submission/v4/submissions/{submissionId}` | `wix-forms-and-payments/submissions` |
34
+ | Submission | `POST /form-submission-service/v4/submissions/namespace/query` (the filter must carry `"namespace": "wix.form_app.form"`) · `GET /form-submission-service/v4/submissions/{submissionId}` | `wix-forms/form/{formId}/submissions` — the `formId` comes off the submission |
33
35
 
34
36
  Example — after creating a form, hand back its links:
35
37
 
36
38
  ```
37
39
  Created "Contact Form".
38
40
  Edit it here: https://manage.wix.com/dashboard/{metaSiteId}/wix-forms/form/{formId}
39
- See submissions: https://manage.wix.com/dashboard/{metaSiteId}/wix-forms-and-payments/submissions
41
+ See submissions: https://manage.wix.com/dashboard/{metaSiteId}/wix-forms/form/{formId}/submissions
40
42
  ```
41
43
 
42
44
  ## Notes
43
45
 
44
- - Creating forms via the [Create Form](./create-form.md) recipe (Form Schemas API) makes them appear in the forms list above.
46
+ - Creating forms via the [Create Form](./create-form.md) recipe (Form Schemas API) makes them appear in the forms list above — **but only when the form was created under the `wix.form_app.form` namespace with Wix Forms (New) installed.** A form created under any other namespace is reachable over the API and completely absent from these pages.
47
+ - **Before handing back a `wix-forms/form/{formId}` link, confirm the form actually renders**: `GET https://www.wixapis.com/form-schema-service/v4/forms/{formId}/summary` must return a non-empty `formSummary.fields` whose count matches every input field. An empty summary means the owner will open this link to a blank form (Create Form, STEP 3).
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: "Update Form"
3
+ description: "Changes a Wix form that already exists, with Form Schemas v4 `PATCH` — add a field to my form, add a dropdown, make a field required or optional, rename a label, reorder or retire a question. Covers reading the form back for its `revision` (and the required `namespace` query parameter), the whole-form body that a `PATCH` needs, the wholesale `formFields` replace that silently soft-deletes anything you omit, changing a field's component type in place, retiring a field that already has submissions, and the read-back that proves what was stored. Use whenever the form exists and the request changes what it collects; use Create Form when there is no form yet."
4
+ ---
5
+ # RECIPE: Update a Wix Form
6
+
7
+ > **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`. Send `wix-site-id: <SITE_ID>` when the token is account-scoped.
8
+
9
+ Change what a form collects — **add a field, relabel one, tighten a rule, reorder, retire one** — on a form that already exists. **Composing the field itself is not here**: [About Form Fields](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/about-form-fields) owns the `identifier` / `inputType` / `componentType` table and the options/enum shapes, and [Create Form](./create-form.md) § Silent breakers apply to an added field exactly as to a created one.
10
+
11
+ **A `PATCH`, never a delete-and-recreate.** An update keeps the `formId` everything downstream already holds — a frontend binding, an automation, a saved dashboard view — and spends no slot against the site's form cap.
12
+
13
+ ---
14
+
15
+ ## The call
16
+
17
+ ```bash
18
+ # 1 · read the form back: you need its current revision, and every field you intend to keep
19
+ curl -X GET \
20
+ 'https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&formIds={formId}' \
21
+ -H 'Authorization: <AUTH>'
22
+
23
+ # 2 · send that form back with your change applied
24
+ curl -X PATCH \
25
+ 'https://www.wixapis.com/form-schema-service/v4/forms/{formId}' \
26
+ -H 'Content-Type: application/json' \
27
+ -H 'Authorization: <AUTH>' \
28
+ -d '{ "form": {
29
+ "id": "{formId}",
30
+ "revision": "<the revision you just read>",
31
+ "name": "<the form name>",
32
+ "namespace": "wix.form_app.form",
33
+ "formFields": [ ...every field, with your change applied... ],
34
+ "steps": [ ...every step, with the layout item for a new field... ]
35
+ } }'
36
+ ```
37
+
38
+ - **⚠️ `namespace` is a REQUIRED query parameter on the read, and this is what a revise run trips over.** Without it the read returns `400 namespace has size 0, expected 10 or more, namespace must not be empty` — a violation naming a *field*, so it reads like a body problem. Fix the call, don't reshape the payload. (The filter on `POST .../v4/forms/query` needs it too.)
39
+ - **The read-back is the body.** Apply your change to what the `GET` returned and send that as `form`. `namespace` is **immutable** and travels along; **there is no `fieldMask`**, and no `revision` outside `form` — inventing either is a silent partial write.
40
+ - **⚠️ Append to `formFields`; never rebuild the entries you read.** `[...form.formFields, newField]` works. Mapping the array to "clean up" each field — dropping `fieldType`, re-shaping an options block — fails with `fieldType cannot be provided with any these fields: input_options, display_options` on **every** field at once. The read-back's field objects are already a valid write shape: pass them through untouched and touch only the one field you are changing.
41
+ - **`formFields` is replaced wholesale.** Wix: *"Any field that's missing from the array you send is moved to `deletedFormFields`, so resend every field you want to keep."* Same for `steps[].layout` — miss an item and that field goes unplaced, which renders empty. Drops are recoverable from `deletedFormFields`, but nothing announces them.
42
+ - **A stale `revision` is rejected** — re-`GET` and re-apply; never guess or increment it.
43
+ - **Plan caps apply to an added field.** `Field count reached its limit of N` on a `PATCH` is the site's premium cap: reduce or upgrade, with the choice put to the user ([Create Form](./create-form.md) § Plan caps). Never split the form across schemas to dodge it.
44
+
45
+ ---
46
+
47
+ ## Adding a field
48
+
49
+ **Start by reading [About Form Fields](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/about-form-fields) § Field types and § Choice fields** for the field kind you're adding: it gives the `identifier` / `inputType` / `componentType` triple — take all three from one row, and note that a dropdown or radio is `inputType: "STRING"` — plus the options/enum shape and the GUID every option needs. [Create Form](./create-form.md) § Choice fields carries a complete dropdown body to copy. Composing a field from memory is what turns one `PATCH` into eight.
50
+
51
+ Then four things are yours to generate:
52
+
53
+ - **`id`** — a fresh lowercase GUID (`uuidgen | tr 'A-Z' 'a-z'`), unique in the form.
54
+ - **`target`** — the immutable submission key, unique across the form: snake_case plus a short random suffix (`job_industry_7f2b`). **A duplicate `target` is accepted and silently stores nothing** for every field but one.
55
+ - **one GUID per choice option** — `options[]` each need their own, exactly like the field's; a readable slug is rejected ([Create Form](./create-form.md) § Choice fields). Generate them with `uuidgen`, never by hand.
56
+ - **the choices, declared twice** — every option needs `id` + `value` + `label`, and `validation.enum` must list every one of those `value`s. **An empty `validation` is a free-text field, not a dropdown**; the general "always include `validation`, even as `{}`" rule does not apply here, and getting it wrong is accepted with a `200`.
57
+ - **the layout item** — append to `steps[<n>].layout.large.items`, mirroring into `medium` / `small` if the form has them:
58
+
59
+ ```json
60
+ { "fieldId": "<the new field's id>", "row": <next row>, "column": 0, "width": 12, "height": 1 }
61
+ ```
62
+
63
+ A field missing from the layout isn't dropped, but it sorts last and has no defined position in the owner's builder. To place it mid-form, insert at that `row` and bump every item at or after it — `row` restarts at 0 in each step.
64
+
65
+ **A choice field's `identifier` is its kind** — `DROPDOWN`, `RADIO_GROUP`, `CHECKBOX_GROUP`, `TAGS` — never `TEXT_INPUT`, which is accepted and stored as a plain text input ([Create Form](./create-form.md) § Choice fields).
66
+
67
+ ---
68
+
69
+ ## The other changes
70
+
71
+ | Change | How |
72
+ |---|---|
73
+ | Label, placeholder, options, validation, order | Patch them and stop — nothing downstream needs updating |
74
+ | A field's component type, in place | Send that field with the **choice `identifier`** and only the new options block; leaving `textInputOptions` beside `dropdownOptions` reads back as the text-input fallback |
75
+ | Retire a field the form has submissions for | Set `hidden: true` — "hidden from submitters": the field stops rendering, and the submissions already collected under it keep their meaning. Dropping it from the array soft-deletes it instead |
76
+ | Remove a field added by mistake this run | Drop it from `formFields`; it lands on `deletedFormFields` |
77
+ | Rename a `target` | **Never.** It is the storage key, so a rename orphans every submission already collected under the old one. Change the *label* instead |
78
+
79
+ ---
80
+
81
+ ## Verify — an update regresses a form exactly as a create can
82
+
83
+ **A `200` on the `PATCH` proves the body parsed, nothing about what was stored.** Read the form back, every time:
84
+
85
+ ```
86
+ GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&formIds={formId}
87
+ GET https://www.wixapis.com/form-schema-service/v4/forms/{formId}/summary
88
+ ```
89
+
90
+ Assert on that read-back, not on the `PATCH` response:
91
+
92
+ - the changed field is there, carrying the `identifier` and `componentType` you sent — **a choice field with a non-empty `options[]`**, which is where a dropdown that degraded to a text input shows up;
93
+ - every field you meant to keep is still present, and every `inputOptions.required` still reads back as sent;
94
+ - `formSummary.fields` still holds one entry per input field — that is what the owner's dashboard renders.
95
+
96
+ Then hand back the dashboard links ([Forms Dashboard Navigation](./forms-dashboard-navigation.md)): the form builder at `wix-forms/form/{formId}`, and that form's submissions at `wix-forms/form/{formId}/submissions`. New submissions carry the new field; past ones don't.
97
+
98
+ ---
99
+
100
+ ## Troubleshooting
101
+
102
+ | Error / symptom | Cause | Fix |
103
+ |---|---|---|
104
+ | `400 namespace has size 0, expected 10 or more` / `namespace must not be empty` on a read | The `namespace` **query parameter** was omitted — it is required on every listing read, and the violation naming a field makes it look like a body problem | Add `?namespace=wix.form_app.form` to the read; leave the payload alone |
105
+ | `fieldType cannot be provided with any these fields: input_options, display_options` — on every field | The read-back's `formFields` were re-mapped or "cleaned" before sending | Send the fields exactly as read and append the new one: `[...form.formFields, newField]` |
106
+ | `inputType enum must be in [UNKNOWN_INPUT_TYPE, STRING, NUMBER, …]` | An invented `inputType` such as `ENUM` (with an `enumOptions` block) for a choice field | A dropdown or radio is `inputType: "STRING"` with `stringOptions.componentType`; multi choice is `ARRAY` |
107
+ | The `PATCH` is rejected for a `revision` mismatch | Someone (or an earlier call) updated the form since your read | Re-`GET` and re-apply the change; never increment the revision yourself |
108
+ | Fields that were on the form are gone after the update | `formFields` was sent partial — the omitted fields moved to `deletedFormFields` | Re-send them in a further `PATCH`, from `deletedFormFields` in the read-back; always send the whole array |
109
+ | Surviving fields render empty for the owner | `steps[].layout` was sent without their items | Re-send every layout item, including `SUBMIT_BUTTON`'s; re-verify with `/summary` |
110
+ | The added dropdown/radio reads back as a text input with `textInputOptions` | `identifier` was `TEXT_INPUT` rather than the choice kind — `componentType` alone doesn't route the renderer | Set `identifier` to the choice kind and re-send. Deleting and re-adding the field with the same `identifier` reproduces it ([Create Form](./create-form.md) § Choice fields) |
111
+ | The update lands but the field never appears in submissions | The new field shares a `target` with an existing one — the server keeps one and the rest store nothing | Give it a unique `target` and `PATCH` again |
112
+ | `Field count reached its limit of N` | The site's premium field cap, counting `INPUT` fields | Reduce, or put the upgrade choice to the user ([Create Form](./create-form.md) § Plan caps) |
113
+
114
+ ---
115
+
116
+ ## Related Documentation
117
+
118
+ - [Create Form](./create-form.md) — composing fields, the identifier table, choice fields, the layout rules, plan caps
119
+ - [Forms Dashboard Navigation](./forms-dashboard-navigation.md) — the builder and submissions links to hand back
120
+ - [Update Form](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/update-form) — the `PATCH` contract and its replace semantics
121
+ - [Form object](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/form-object) — `revision`, `hidden`, `deletedFormFields`, `steps[].layout`
122
+ - [About Form Fields](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/about-form-fields) — every field-level rule
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: "Generate an Image with AI"
3
+ description: Generates an image from a text prompt with the Wix AI APIs (Runware). Returns a short-lived URL that must be imported to be kept — importing is Upload Media to Wix's job, and this recipe hands off to it. Covers choosing a model and its cost/latency/content-filter trade-off, the accepted output sizes, per-model batching limits, the AI credit each call spends, and why a content refusal arrives as a success response with no image.
4
+ ---
5
+ # RECIPE: Generate an Image with AI
6
+
7
+ For an image the user does not have. An image they supplied — an attachment or a public URL — is uploaded instead: [Upload Media to Wix](upload-media-to-wix.md).
8
+
9
+ **API reference:** [About the Wix AI APIs](https://dev.wix.com/docs/api-reference/articles/ai-tools/ai-apis/about-the-wix-ai-apis), *Generating images* — endpoint, auth, and the full model table. Developer Preview, so confirm the contract there.
10
+
11
+ ---
12
+
13
+ ## AI credits
14
+
15
+ Each call spends roughly **one AI credit**, billed to the site owner, or to the Wix user who installed the calling app. `numberResults: n` spends n. Importing is free.
16
+
17
+ Generate one image per entity that needs one, and say how many a run will generate before starting it. Regenerate only when the user asks.
18
+
19
+ ---
20
+
21
+ ## Step 1: Generate
22
+
23
+ ```bash
24
+ curl -X POST 'https://www.wixapis.com/runwareschemaless/v1/request' \
25
+ -H 'Content-Type: application/json' \
26
+ -H 'Authorization: <AUTH>' \
27
+ -H 'wix-site-id: <METASITE_ID>' \
28
+ -d '[{
29
+ "taskType": "imageInference",
30
+ "taskUUID": "3f8c1e42-9b7a-4d16-8a5e-2c0f7b9d4e11",
31
+ "outputType": "URL",
32
+ "outputFormat": "PNG",
33
+ "positivePrompt": "a ceramic pour-over coffee dripper on a walnut counter, morning light",
34
+ "width": 1024,
35
+ "height": 1024,
36
+ "model": "runware:400@1",
37
+ "numberResults": 1
38
+ }]'
39
+ ```
40
+
41
+ | Parameter | Value |
42
+ |---|---|
43
+ | `taskUUID` | A real UUIDv4. Anything else returns `400 invalidTaskUUID` |
44
+ | `width` / `height` | `1024×1024` (square — products, services, menu items), `1376×768` (16:9 hero), `1200×896` (4:3 editorial). Free-form sizes return 400 |
45
+ | `model` | See below |
46
+ | `numberResults` | `1` |
47
+
48
+ Omit `steps` and `CFGScale`: Runware accepts them, other models return `400 unsupportedParameter`.
49
+
50
+ ### Models
51
+
52
+ | `model` | Latency | Content filter |
53
+ |---|---|---|
54
+ | `runware:400@1` | ~5s | Loosest — the default |
55
+ | `google:4@2` | ~25s | Medium, best fidelity |
56
+ | `bfl:5@1` | — | Strictest; refuses trademark-ish prompts |
57
+
58
+ Any Runware-supported model works — the reference's *Suggested models* table has the current AIR IDs.
59
+
60
+ ### Read the response
61
+
62
+ ```json
63
+ { "data": [ { "imageURL": "https://im.runware.ai/image/ws/2/ii/<id>.png" } ] }
64
+ ```
65
+
66
+ **A refusal is a success response with no `imageURL`** — test for the field, not the status. On a refusal, reword the prompt or switch model, and report the image as not created.
67
+
68
+ ---
69
+
70
+ ## Step 2: Import
71
+
72
+ `imageURL` expires, so import it as it lands. [Upload Media to Wix](upload-media-to-wix.md) owns that endpoint and its `PENDING` / `READY` rules; pass the `imageURL` as `url` and `image/png` as `mimeType`.
73
+
74
+ The result is the returned `file.id` / `file.url` — permanent, and what an entity field takes.
75
+
76
+ ---
77
+
78
+ ## Batches
79
+
80
+ `runware:400@1` and `bfl:5@1` take several tasks in one body. `google:4@2` returns 504 at three or more, so fire parallel single-task requests for it.
81
+
82
+ Failure is per image: attach the ones that succeeded, create the rest of the entities text-only, and name which entities have no image and why. A failed image stays missing rather than being swapped for another.
83
+
84
+ ---
85
+
86
+ For JavaScript callers there is an SDK path — `generateImage()` from the Vercel AI SDK with `runware.image(...)` from `@wix/ai`: [Set up the Wix AI SDK](https://dev.wix.com/docs/api-reference/articles/ai-tools/ai-apis/set-up-the-wix-ai-sdk).
@@ -20,6 +20,19 @@ Canonical example:
20
20
  - Assistant: “What product would you like to create? You can upload up to 3 images and I’ll generate the product information from them, or describe the product in text.”
21
21
  - Result: **Stop. Do not request structured product fields yet.**
22
22
 
23
+ ## Sections
24
+
25
+ - [Preflight](#preflight-respond-and-return-before-mutation) — whether this turn may mutate at all, and what to answer when it may not
26
+ - [Core flow](#core-flow) — the order the rest of this recipe runs in
27
+ - [Choose the endpoint](#choose-the-endpoint) — single vs bulk, with or without inventory
28
+ - [Build each product](#build-each-product) — the product object: type, media, options, variants, prices, SKUs
29
+ - [Generated descriptions and SEO](#generated-descriptions-and-seo) — what copy written from an image may claim
30
+ - [Add images](#add-images) — uploading a supplied image, or generating one
31
+ - [Wrap the request](#wrap-the-request) — the envelope the chosen endpoint expects
32
+ - [Request projected media](#request-projected-media-when-validating-images) — which fields to project so images are proven on create
33
+ - [Validate before reporting success](#validate-before-reporting-success) — what counts as proof, and what may be claimed
34
+ - [Failure guide](#failure-guide) — reading a rejection
35
+
23
36
  ## Preflight: respond and return before mutation
24
37
 
25
38
  Name and price are mandatory **workflow inputs for every product**, even when the API schema accepts an omitted price or initializes it to zero. Evaluate the cases below in order. When a case says **RETURN**, end the turn immediately: do not call a create endpoint, do not continue to examples, and do not create first and ask afterward.
@@ -180,7 +193,9 @@ With no file supplied, create the product and report it as not sellable until on
180
193
 
181
194
  ## Add images
182
195
 
183
- Skip this step when no image was supplied. Prefer the available dedicated Wix image-upload capability and upload all attachments and public image URLs together. Do not reconstruct that capability inside a general API-execution call. When no dedicated uploader is available, use `POST /site-media/v1/files/import` with the external URL in the body field named exactly `url`—not `importUrl`:
196
+ Skip this step when no image was supplied — unless the user asked for one to be generated, in which case create it first with [Generate an Image with AI](../media/generate-image-with-ai.md) and continue below with the Media Manager file it returns. Do not offer generation unprompted, and never generate a substitute for a supplied image that failed to upload.
197
+
198
+ Prefer the available dedicated Wix image-upload capability and upload all attachments and public image URLs together. Do not reconstruct that capability inside a general API-execution call. When no dedicated uploader is available, use `POST /site-media/v1/files/import` with the external URL in the body field named exactly `url`—not `importUrl`:
184
199
 
185
200
  ```json
186
201
  {"url":"https://example.com/product.jpg","mimeType":"image/jpeg","displayName":"Product.jpg","mediaType":"IMAGE"}
@@ -114,4 +114,8 @@ Seeding is **admin-only** — not part of the client, which is built solely per
114
114
  For any later admin/management request, work as in STEP 4: your vertical's seed module first, else
115
115
  `wix-base44-connector` doc discovery — all over the connector.
116
116
 
117
+ **A change to what the business data COLLECTS is one of these, however UI it sounds** — a new form
118
+ field, product option or service setting. Change it on Wix first per the vertical's `seed/SEED.md`,
119
+ then edit the client against the verified result.
120
+
117
121
  version: v1341
@@ -15,6 +15,12 @@ field — the shipped utils supply the schema behind it, the state, the validati
15
15
  > Use **`cms`** only when the app must **read the entries back** (a gallery, a listing, "my
16
16
  > submissions") — a visitor cannot read Forms submissions. Submit-only → forms.
17
17
 
18
+ > **⚠️ "Add a field" is a SCHEMA change.** *"Add a dropdown for job industry"*, *"make the phone
19
+ > optional"*, *"drop the budget question"* — all reads like UI work and all are a `PATCH` to the live
20
+ > schema (`seed/SEED.md`, REVISE): patch it, verify, rewrite `wix-forms.config.js`, **then** wire the
21
+ > control. A field the visitor can fill that isn't in the schema is **silent data loss** — the value
22
+ > reaches no submission, inbox or contact, and the submit still resolves `true`.
23
+
18
24
  ## What ships (utils only)
19
25
 
20
26
  | file | what it is |
@@ -24,7 +30,7 @@ field — the shipped utils supply the schema behind it, the state, the validati
24
30
  | `rest/wix-forms-submissions.js` | Write transport: `toSubmissionValues`, `createSubmission`, `SUBMITTED_OK`, the file flow (`getMediaUploadUrl`, `uploadSubmissionFile`, `uploadFormFiles`), `submissionViolations`. |
25
31
  | `hooks/useWixForm.js` | The form's non-visual half: `useWixForm(formId)` → `{ form, values, setValues, bind, submit, validate, errors, loading, read }`. Also exports `validateField`, `mapSubmissionErrors`, `FORM_ERROR`. Not on React? Those plus `lib/` are the load-bearing part; port the state around them. |
26
32
  | `wix-config.js` *(shared)* | the two ids, written by the install step. |
27
- | `wix-forms.config.js` *(generated)* | **the gate** — `WIX_FORMS`: each form's `formId` + read-back `targets`, written by the seed after verification. |
33
+ | `wix-forms.config.js` *(generated)* | **the gate** — `WIX_FORMS`: each form's `formId` + read-back `targets`, written by the seed after verification. Its one later edit is a verified schema change (REVISE), never a hand-typed field. |
28
34
  | `WixManageBanner` *(shared)* | Dev-only manage banner — mount it in your Layout. |
29
35
 
30
36
  ## Prerequisites — a HARD GATE
@@ -384,6 +390,9 @@ Don't reach for `role="form"`, an `aria-label` on an input that already has a `<
384
390
  ## Hard rules
385
391
 
386
392
  - A visitor-fillable form is this vertical, not `cms`.
393
+ - **Never hold a form field in local component state** — a value outside the hook's `values` submits
394
+ nothing. Adding a field is a schema change: `seed/SEED.md`, REVISE.
395
+ - **Never answer a field request with a dashboard link** — that link is for the owner's own edits.
387
396
  - Read and write only through the shipped helpers — never hand-build a Wix Forms URL.
388
397
  - Never invent a `target`, label, option or constraint — every one comes from the schema (authored
389
398
  markup included). Never detect a control by its `target` name either.
@@ -412,8 +421,8 @@ Don't reach for `role="form"`, an `aria-label` on an input that already has a `<
412
421
 
413
422
  Three sources answer what this file doesn't: the **JSDoc** in `hooks/useWixForm.js` and
414
423
  `lib/wix-form-schema-utils.js`; the **Wix reference** below; and `seed/SEED.md` for how a field was
415
- authored. Calling an endpoint the helpers don't wrap is fine via `wixApiRequest` — look up the exact
416
- method and body first (or use the `wix-docs` skill), never guess.
424
+ authored, and REVISE for changing one. Calling an endpoint the helpers don't wrap is fine via
425
+ `wixApiRequest` — look up the exact method and body first (or the `wix-docs` skill), never guess.
417
426
 
418
427
  - Form object (the shape `form` and its fields arrive in): https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/form-object.md
419
428
  - Form Schemas API: https://dev.wix.com/docs/api-reference/crm/forms/form-schemas.md
@@ -438,21 +447,28 @@ checks the dashboard summary, sends and deletes one real submission), then write
438
447
  `src/rest/wix-forms.config.js`. Needs an elevated credential, and **does not run in parallel with the
439
448
  client** — see Prerequisites.
440
449
 
450
+ **Changing a form that already exists** — a new field, a relabel, a tightened rule — is that doc's
451
+ **REVISE** action: `PATCH` on the current `revision`, re-verify, rewrite the config, wire the
452
+ control. Never a second create.
453
+
441
454
  ## Point the user to their dashboard
442
455
 
443
456
  Substitute the site's `metaSiteId`:
444
457
 
445
458
  - **Forms list** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms`
446
- - **Form builder** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms/form/{formId}` → add,
447
- relabel, reorder or require fields; every change reflects on the site with no code change.
448
- - **Submissions** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms-and-payments/submissions`
449
- - **Forms settings** (incl. notifications) — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms-and-payments/settings`
459
+ - **Form builder** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms/form/{formId}` → where
460
+ the **owner** edits fields; a relabel or new option reaches a looped form with no code change, a new
461
+ field still needs a control. **Asked to change the form yourself? REVISE, not this link.**
462
+ - **Submissions** — `https://manage.wix.com/dashboard/{metaSiteId}/wix-forms/form/{formId}/submissions`
463
+ → per form, off its builder path; there's no site-wide submissions page to link
450
464
  - **Contacts** — `https://manage.wix.com/dashboard/{metaSiteId}/contacts` → contact-mapped fields
451
465
  create or update a contact automatically.
452
466
 
453
467
  ## Verify
454
468
 
455
469
  - [ ] `wix-forms.config.js` exists; every `formId` comes from `WIX_FORMS`, none typed by hand.
470
+ - [ ] Every control's `name` inside the `<form>` is a target in `WIX_FORMS.<key>.targets`, and its
471
+ value lives in the hook's `values` — **no field is backed by local state**.
456
472
  - [ ] `WIX_CLIENT_ID` set (not the placeholder).
457
473
  - [ ] Looped form: relabel a field in the dashboard, reload, the new label appears with no code
458
474
  change. Authored form: every `target` in the JSX matches `WIX_FORMS.<key>.targets` exactly.
@@ -1,11 +1,23 @@
1
1
  # Forms — seeding (Form Schemas v4 REST)
2
2
 
3
- Creating a form is an **admin/build-time** job, in six sequential steps. Each has a section below.
3
+ **Two actions live in this doc — decide which one you're doing before reading on.** Both are
4
+ **admin/build-time** work over an elevated credential, and both run through the same six steps:
5
+
6
+ | action | when | the steps it uses |
7
+ |---|---|---|
8
+ | **CREATE a form** — `POST` | there's no form yet: the app needs a contact / quote / application form and nothing has been created for it | all six, in order |
9
+ | **REVISE a form** — `PATCH` | the form already exists and a request changes **what it collects**: *"add a dropdown for job industry"*, *"make the phone optional"*, *"add a file upload"*, *"drop the budget question"* | 1 (auth) → 3 (author just the new field) → **4b · Revising a form later** → 5 (re-verify) → 6 (rewrite the config, then wire the control) |
10
+
11
+ A field the visitor can fill that isn't in the schema is **silent data loss** — no submission, no
12
+ inbox, no contact, and the submit still resolves `true`. So never local component state, and never
13
+ delete-and-recreate a live form: **`PATCH` it**, keeping the `formId` the UI already imports.
14
+
15
+ The six steps, each with a section below:
4
16
 
5
17
  1. **Authenticate** — get an elevated credential; the public client ID cannot write.
6
- 2. **Install the Wix Forms app** — idempotent, and nothing works until it's there.
18
+ 2. **Install the Wix Forms app** — idempotent, and nothing works until it's there. *(create only.)*
7
19
  3. **Author the form body** — adapt the every-field payload to what the request asks for.
8
- 4. **Create the form** — POST each body; a `200` here means almost nothing.
20
+ 4. **Create the form** — POST each body; a `200` here means almost nothing. *(revising: **4b**.)*
9
21
  5. **Verify it works** — read the form back, and **submit to it once** for real.
10
22
  6. **Hand off** — the verified `formId` + field `target`s are written to a file the UI imports.
11
23
 
@@ -22,7 +34,7 @@ live Wix API reference.
22
34
  | 3 | Create Form | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/create-form.md | the create call, plus 10 complete request examples — the source of the payload below |
23
35
  | 3 | Form object | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/form-object.md | each property individually, incl. form-level settings this doc doesn't cover |
24
36
  | 4 | Form Schemas API | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas.md | every schema method (get, list, delete, enable/disable) |
25
- | 4 | Update Form | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/update-form.md | revising a form without losing its `formId` |
37
+ | 4b | Update Form | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/update-form.md | the `PATCH`: revising a form without losing its `formId`, and the whole-array replace semantics |
26
38
  | 4 | List Forms Providers Configs | https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/list-forms-providers-configs.md | the Forms **app's** ceiling on fields and forms — **not** the site's plan cap, which this call cannot tell you (see step 4) |
27
39
  | 5 | Create Submission | https://dev.wix.com/docs/api-reference/crm/forms/form-submissions/create-submission.md | the submission call used to prove the form accepts data |
28
40
  | 5 | About Submission Values | https://dev.wix.com/docs/api-reference/crm/forms/form-submissions/about-submission-values.md | the value shape each `inputType` submits — string, array, address object, … |
@@ -77,8 +89,10 @@ costs nothing.
77
89
  ## 3 · Author the form body — every field type
78
90
 
79
91
  There is **one** create call:
80
- `POST https://www.wixapis.com/form-schema-service/v4/forms` with `{ "form": { … } }`.
81
- **You author that body**, starting from the payload below and adapting it to the request.
92
+ `POST https://www.wixapis.com/form-schema-service/v4/forms` with
93
+ `{ "form": { "namespace": "wix.form_app.form", … } }` — that namespace is the Wix Forms app's, and
94
+ the only one the owner's dashboard shows. **You author that body**, starting from the payload below
95
+ and adapting it to the request.
82
96
 
83
97
  The payload is one form holding **every field type Wix Forms supports**. Each field block is copied
84
98
  verbatim from the official Create Form examples, so the `identifier` / `inputType` / `componentType`
@@ -155,10 +169,18 @@ owner's dashboard, or on the first real submission.
155
169
  - **`required` lives at `inputOptions.required`**, never inside a validation block.
156
170
  - **`validation` is always present**, even as `{}`, and nests under the **`inputType`** options
157
171
  object — not the `componentType` one.
172
+ - **A choice field's `identifier` IS its kind** — `DROPDOWN`, `RADIO_GROUP`, `CHECKBOX_GROUP`,
173
+ `TAGS` — matching the `componentType`, never `TEXT_INPUT`: the identifier routes the field to its
174
+ renderer, so `TEXT_INPUT` + `componentType: DROPDOWN` is accepted and stored as a text input. (A
175
+ long answer is `TEXT_AREA` for the same reason.)
158
176
  - **A choice field declares its options twice** — the component's `options[]` and the validation
159
177
  `enum` (`STRING`) or `items.stringOptions.enum` + `itemType` (`ARRAY`) — and the two must agree.
160
178
  Get it wrong and the create still returns `200`: the field is created as a **plain text box**,
161
179
  losing its choices. Only the 5a component check catches it.
180
+ - **A choice field that came back as a text box: read which options block came home.**
181
+ `textInputOptions` where you sent `dropdownOptions` is that fallback — and with a well-formed block
182
+ and `enum`, the cause is the `identifier`. Re-sending it, or delete-and-re-add with the same
183
+ identifier, reproduces it.
162
184
  - **`target` is the immutable submission key**: starts with an ASCII letter, letters/digits/`_`
163
185
  only, no `__`, unique within the form. Use field's label converted to snake_case with _ and 6 random alphanumeric
164
186
  characters as the field target, e.g. "First name" -> `first_name_a689be`.
@@ -1578,7 +1600,7 @@ always on the public host, never `/_api/`:
1578
1600
 
1579
1601
  ```
1580
1602
  POST https://www.wixapis.com/form-schema-service/v4/forms
1581
- { "form": { … the body from step 3 … } }
1603
+ { "form": { "namespace": "wix.form_app.form", … the rest of the body from step 3 … } }
1582
1604
  ```
1583
1605
 
1584
1606
  Read **`form.id`** from the response — that's the `formId` the rest of the run carries. **Record it
@@ -1625,16 +1647,64 @@ proves the form works. Two failures worth recognizing before you retry:
1625
1647
  than discovering the limit after a full authoring pass, and count every entry in `formFields`, the
1626
1648
  `SUBMIT_BUTTON` included.
1627
1649
 
1628
- **Revising a form later** — add a field, relabel one, tighten a rule:
1650
+ ## 4b · Revising a form later
1651
+
1652
+ Add a field, relabel one, tighten a rule, retire one — a `PATCH`, never a second create. Everything
1653
+ above still applies; this is the delta.
1654
+
1655
+ **Read the form back first — you need its `revision`.** The `formId` comes from
1656
+ `src/rest/wix-forms.config.js` (`WIX_FORMS.<key>.formId`), never typed by hand:
1629
1657
 
1630
1658
  ```
1659
+ GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&formIds=<formId>
1631
1660
  PATCH https://www.wixapis.com/form-schema-service/v4/forms/<formId>
1632
- { "form": { …, "revision": "<the form's current revision>" } }
1661
+ { "form": { …the whole object you just read, with your change…, "revision": "<its current revision>" } }
1633
1662
  ```
1634
1663
 
1635
- Never delete-and-recreate: a `PATCH` keeps the `formId` the UI already holds and spends no extra slot
1636
- against the site's form cap. Re-run step 5 afterwards — an update regresses a layout exactly as a
1637
- create can.
1664
+ - **`namespace` is a required query parameter on the read**, hence the `?namespace=` above. Without it
1665
+ the read `400`s with `namespace has size 0, expected 10 or more` — a violation naming a *field*, so
1666
+ it reads like a body problem. Fix the call, not the payload.
1667
+ - **The read-back is the `PATCH` body.** Apply the change to what the `GET` returned and send that.
1668
+ `namespace` is immutable and travels along; **there is no `fieldMask`**, and no `revision` outside
1669
+ `form`. Inventing either is a silent partial write.
1670
+ - **`formFields` replaces wholesale.** Wix: *"Any field that's missing from the array you send is
1671
+ moved to `deletedFormFields`, so resend every field you want to keep."* `steps` behaves the same
1672
+ way. Drops are recoverable from `deletedFormFields` — if you notice, which is step 5's job.
1673
+ - **A stale `revision` is rejected** — re-`GET` and re-apply; never guess or increment it.
1674
+ - **A new field needs three things generated**: a fresh UUID `id`, a `target` unique in the form
1675
+ (`job_industry_7f2b`), and — for a choice field — an options block whose `enum` and `options[]`
1676
+ agree. Step 3 is the shape, and its warnings still hold.
1677
+
1678
+ **Add the layout item too** — append to `steps[<n>].layout.large.items`, mirroring into `medium` /
1679
+ `small` if the form has them:
1680
+
1681
+ ```json
1682
+ { "fieldId": "<the new field's id>", "row": <next row>, "column": 0, "width": 12, "height": 1 }
1683
+ ```
1684
+
1685
+ A field missing from the layout isn't dropped, but it sorts last and has no defined position in the
1686
+ owner's builder. To place it mid-form, insert at that `row` and bump every item at or after it — `row`
1687
+ restarts at 0 per step.
1688
+
1689
+ **Changing a field's component type in place** — a text input that should have been a dropdown — means
1690
+ the choice `identifier` and **only** the new options block; don't leave `textInputOptions` beside
1691
+ `dropdownOptions`. A read-back still showing `textInputOptions` is the identifier, not the block.
1692
+
1693
+ **The other revisions:**
1694
+
1695
+ - **label, placeholder, options, validation, order** — patch and stop. The client reads all of those
1696
+ live from the schema, so no config rewrite and no code change follows.
1697
+ - **retiring a field the form has submissions for** — set `hidden: true` ("hidden from submitters":
1698
+ the field stops being rendered, and the submissions already collected under it keep their meaning)
1699
+ rather than dropping it from the array. Drop only a field added by mistake in this same run.
1700
+ - **never rename a `target`** — it's the storage key, so a rename orphans every submission already
1701
+ collected under the old one. Change the *label* instead.
1702
+
1703
+ **Then re-run step 5 in full** — a `PATCH` regresses a form exactly as a create does, and §5a is where
1704
+ a dropdown that silently became a text input gets caught. Then step 6, revising: add the read-back
1705
+ `target` to `src/rest/wix-forms.config.js` (the one sanctioned edit to that file outside a seed run,
1706
+ and only *after* step 5 passes) and wire the control per `INSTRUCTIONS.md`. The field is live for new
1707
+ submissions; past ones don't carry it. Step 4's plan gates apply to an added field too.
1638
1708
 
1639
1709
  ## 5 · Verify it works
1640
1710
 
@@ -1671,6 +1741,9 @@ against the returned `form.formFields`:
1671
1741
  GET https://www.wixapis.com/form-schema-service/v4/forms/<formId>/summary
1672
1742
  ```
1673
1743
 
1744
+ *(The summary and the single-form `GET` take the id in the path, so neither needs the `namespace`
1745
+ query parameter — every **listing** read does.)*
1746
+
1674
1747
  Assert `formSummary.fields` is **non-empty**, with one entry per input field you sent (everything in
1675
1748
  `formFields` except the `SUBMIT_BUTTON`). The summary is exactly what the Wix dashboard renders, so a
1676
1749
  short or empty count means the form is blank — or partly blank — **for the owner**, even while the
@@ -1735,8 +1808,8 @@ DELETE https://www.wixapis.com/form-submission-service/v4/submissions/<id>
1735
1808
  The id comes from the 5c response as **`submission.id`**: REST returns `id`, not the `_id` the
1736
1809
  reference schema shows (that's the SDK's shape). Do this every time — the owner's inbox is a real
1737
1810
  business inbox, and a probe left in it looks like a real enquiry. If a probe ever escapes, find it
1738
- with `POST /form-submission-service/v4/submissions/namespace/query` filtered by `formId` +
1739
- `namespace`.
1811
+ with `POST /form-submission-service/v4/submissions/namespace/query`, filtered by `formId` and
1812
+ `"namespace": "wix.form_app.form"`.
1740
1813
 
1741
1814
  ## 6 · Hand off
1742
1815
 
@@ -1745,8 +1818,9 @@ form, keyed by the form's name lowercased with each run of non-alphanumerics col
1745
1818
  `_` (`"Quote request"` → `quote_request`):
1746
1819
 
1747
1820
  ```js
1748
- // Written by the seed run once step 5 passed. Do not hand-edit, and do not create it early:
1749
- // its existence is what proves the form schemas were created AND verified.
1821
+ // Written by the seed run once step 5 passed. Do not create it early: its existence is what proves
1822
+ // the form schemas were created AND verified. Its only later edit is a verified schema change —
1823
+ // see step 4b; never a hand-typed field.
1750
1824
  export const WIX_FORMS = {
1751
1825
  contact: { formId: "…", name: "Contact", targets: { email: true, message: true } },
1752
1826
  };