@wix/agent-skills 1.31.2 → 1.32.0

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.
Files changed (40) hide show
  1. package/README.md +4 -12
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +39 -15
  4. package/skills/wix-app/references/BACKEND_API.md +1 -1
  5. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +11 -8
  6. package/skills/wix-app/references/SDK_MODULE_MAP.md +1 -1
  7. package/skills/wix-app/references/STORES_VERSIONING.md +24 -2
  8. package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +9 -13
  9. package/skills/wix-app/references/dashboard-page/IMAGE_RENDERING.md +61 -0
  10. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +54 -64
  11. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +14 -21
  12. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +37 -30
  13. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +43 -29
  14. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +40 -39
  15. package/skills/wix-app/references/editor-react-component/PARTS.md +8 -6
  16. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +22 -2
  17. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +39 -16
  18. package/skills/wix-app/scripts/sdk-curated.json +45 -0
  19. package/skills/wix-app/scripts/sdk-lookup.cjs +1180 -0
  20. package/skills/wix-base44-connector/SKILL.md +4 -2
  21. package/skills/wix-base44-connector/scripts/utils.cjs +5 -2
  22. package/skills/wix-headless-kit/SKILL.md +4 -1
  23. package/skills/wix-headless-kit/guides/api-run.md +14 -20
  24. package/skills/wix-headless-kit/install/pins.json +1 -1
  25. package/skills/wix-manage/SKILL.md +30 -8
  26. package/skills/wix-manage/references/automations/actions.md +134 -0
  27. package/skills/wix-manage/references/automations/activation-status.md +64 -48
  28. package/skills/wix-manage/references/automations/api-catalog.md +213 -77
  29. package/skills/wix-manage/references/automations/automation-model.md +199 -0
  30. package/skills/wix-manage/references/automations/bracket-expressions.md +132 -0
  31. package/skills/wix-manage/references/automations/build-and-manage-automations.md +305 -26
  32. package/skills/wix-manage/references/automations/triggers.md +190 -0
  33. package/skills/wix-manage/references/collaborators/manage-site-collaborators.md +128 -0
  34. package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +3 -1
  35. package/skills/wix-manage/references/forms/read-forms-and-submissions.md +277 -0
  36. package/skills/wix-manage/references/forms/update-form.md +5 -0
  37. package/skills/wix-manage/references/seo/manage-url-redirects.md +33 -4
  38. package/skills/wix-manage/references/sites/create-headless-site.md +14 -5
  39. package/skills/wix-manage/references/sites/upload-static-site.md +373 -302
  40. package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +3 -3
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: "Automations Trigger Configuration"
3
+ description: "Select and configure automation triggers, trigger filters and event payload schemas using the site catalog."
4
+ ---
5
+
6
+ > **Stage scope:** this stage covers only what [Build and Manage Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/skills/build-and-manage-wix-automations) lists; do not perform anything marked "Later stages only" or any workflow that needs a guide marked "not yet published". "Stage", "Later stages only" and "not yet published" describe how this skill is being rolled out; never mention them to the user. Explain a limit in product terms: what can't be done through the API here and where the user can do it instead (for example, in the automations builder).
7
+
8
+ # Triggers — Discovery, Payload Schema, Filters
9
+
10
+ **TL;DR**
11
+
12
+ - A trigger is `appId` (GUID) + `triggerKey` (string). Find real ones with **Resolve Triggers** (installed apps only); never invent a key or reuse one from an example.
13
+ - There is no trigger `description` field. Choose by `displayName` + `triggerKey` + **payload schema** + available **filters** — names are ambiguous.
14
+ - Only filters listed in the trigger's `filters` array can be used. A payload field without a filter definition is NOT filterable (use a condition step instead — see Automations Conditions (not yet published)).
15
+ - A saved filter is `{ id, fieldKey, filterExpression }` with one of exactly two builder-readable expression shapes (§4.2). Values are always a literal array. Entity values are **ids**, never names.
16
+ - Required filters, and required follow-ups of any filter you set, must be present. The builder writes BOOLEAN (and defaulted NUMBER) filters itself — save them too (§4.1).
17
+ - Triggers with `implementedMethods.getDynamicSchema: true` expose more payload fields once their schema-changing filters are chosen — fetch the dynamic schema (§5) before mapping anything downstream.
18
+
19
+ ---
20
+
21
+ ## 1. APIs used here
22
+
23
+ Site-scoped (`wix-site-id` header), SDK module `triggerCatalog`: Resolve Triggers (installed apps) · Get Trigger By App Id And Key (filters, payload schema) · Get Trigger Dynamic Schema · Get Identities Schema · Query Triggers (whole catalog, regardless of installation). Paths and request shapes: [Automations API Catalog](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-api-catalog) §3.
24
+
25
+ ## 2. Finding the right trigger
26
+
27
+ 1. **List cheaply**: Resolve Triggers with `basicFieldsOnly: true` (drops schemas and filters) and pages of ≤ 25; page until `paging.total` is reached, de-duplicating ([Automations API Catalog](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-api-catalog) §6).
28
+ 2. **Narrow**: when you know the app, pass `appId` (returns all that app's triggers regardless of maturity; the app must be installed). You may also put `appId` / `triggerKey` in `query.filter`; if the filter is rejected, page and filter locally.
29
+ 3. **Search locally** over `displayName` and `triggerKey`: the user's phrase → the domain noun ("booking", "form", "order", "member") → synonyms ("appointment"/"session", "purchase"/"order placed"). Keys usually start with the app prefix (`wix_bookings-`, …).
30
+ 4. **Fetch details** for the 1–3 best candidates only: Get Trigger By App Id And Key.
31
+ 5. **Disambiguate by schema, not name** ("Session booked" vs "Appointment request approved" for "booking confirmed"): read `payloadDataSchema` titles and `filters` to confirm the event and that the downstream data exists. If two remain equally plausible and it matters, ask.
32
+ 6. Only in Query Triggers, not Resolve Triggers → its app isn't installed (tell the user to install it) or, with `maturity` `CREATED` / not GA, it isn't available to sites yet (§5 of Automations Feasibility and Planning (not yet published)).
33
+
34
+ Selection guidance for overlapping families:
35
+
36
+ - Compare, in order: which trigger offers the filters you need → which exposes the payload (or dynamic schema) you need → site context → default to the newer/standard family.
37
+ - Forms: default to the **Wix Forms App** family — app `225dd912-7dea-4738-8688-4b8c6955ffc2`, key `wix_form_app-form_submitted`. Use the older app `14ce1214-b278-a7e4-1373-00cebd1bef7c`, key `wix_forms-form_submit` only when the site's forms live there (check which app the form id comes from). For a named form or choice value, prefer the trigger whose filter expresses it over a generic trigger + condition.
38
+ - Scheduled trigger → §4 of Automations Schemas and Scheduling (not yet published). Webhook / custom trigger → later stages only (not in stage 2).
39
+
40
+ ## 3. Reading the trigger object
41
+
42
+ | Field | Use |
43
+ | -------------------------------------------------- | --------------------------------------------------------------------------- |
44
+ | `appId`, `triggerKey` | Copy verbatim into `configuration.trigger`. |
45
+ | `displayName` | What the user sees. Not a unique key. |
46
+ | `payloadDataSchema` | JSON Schema of the event payload — the only trigger data you may reference. |
47
+ | `filters[]` (≤5, each with `followUpFilters[]` ≤5) | The only filterable fields (§4). |
48
+ | `implementedMethods.getDynamicSchema` | `true` → payload grows with filter choice (§5). |
49
+
50
+ Payload schema annotations you will meet:
51
+
52
+ - `format`: `uuid`, `date-time`, `date`, `email`, `uri`, `number` (a numeric string — wrap it in `toNumber()` in numeric functions).
53
+ - `identityType: "contact" | "member"` on an id field → the aggregated payload also contains a root-level `contact` / `member` object (keys and rules: [Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model) §4). `visitor` ids stay plain fields with no enrichment.
54
+ - `itemsSelectionConfiguration.providerKey` / `.tag` → the field holds an **entity id** (service, form, label, product, …). Compare it only to ids (see Automations Entity and Provider Configuration (not yet published)).
55
+ - `wixCustomType` fixed shapes: `MONEY` `{value: string, currency: string}` · `ORDER_ID`/`ORDER_PAYMENT_ID`/`RECEIPT_ID`/`RECEIPT_PRESET_ID` uuid strings · `ATTACHMENT` `{fileName, downloadUrl}` · `IMAGE_URL` uri string · `FIELDS` array of `{label, value}` · `RECEIPT` complex object.
56
+ - `futureDate: true` on a date-time field → the trigger supports a "before the event" offset (§5 of Automations Schemas and Scheduling (not yet published)).
57
+ - `hidden: true` → not shown in pickers but still referenceable. `examples` show real value shapes.
58
+
59
+ ## 4. Filters
60
+
61
+ ### 4.1 Filter definition (catalog)
62
+
63
+ ```json
64
+ {
65
+ "id": "<filter-uuid>",
66
+ "fieldKeyType": "STATIC_FIELD_KEY",
67
+ "staticFieldKey": "service_id",
68
+ "valueInput": {
69
+ "type": "ENTITY_SELECTOR",
70
+ "label": "Select services",
71
+ "required": false,
72
+ "reevaluateDynamicSchema": true,
73
+ "entitySelector": {
74
+ "id": "<provider-key>",
75
+ "multiSelect": true,
76
+ "queryFieldToFilterIdMapping": {},
77
+ "queryFieldToValueMapping": {}
78
+ }
79
+ },
80
+ "followUpFilters": []
81
+ }
82
+ ```
83
+
84
+ Values you put in the expression, by `valueInput.type` (anything else is dropped or rewritten when the panel opens):
85
+
86
+ - `ENTITY_SELECTOR`: entity ids as **quoted strings**, of the kind named by `entitySelector.id` / the field's `itemsSelectionConfiguration` — from the vertical's public API or the user (Automations Entity and Provider Configuration (not yet published)). `multiSelect: false` → one id.
87
+ - `STATIC_ITEMS`: quoted strings equal to a `staticValues[].value` that has a `displayName` (not the `displayName`, not the item `id`, never bare numbers).
88
+ - `USER_INPUT` `NUMBER`: exactly ONE bare number within `numberInputOptions.minValue`/`maxValue`. `BOOLEAN`: exactly ONE bare `true`/`false`.
89
+
90
+ The builder writes some filters by itself when the trigger panel opens; save them too or the automation shows an unsaved change: every `USER_INPUT` `BOOLEAN` filter (user's value, else `booleanInputOptions.defaultValue`, else `false`) and every `NUMBER` filter that has a numeric `defaultValue` (user's value, else that default).
91
+
92
+ ### 4.2 Saved filter and the two allowed expression shapes
93
+
94
+ ```json
95
+ {
96
+ "id": "<same filter id as the catalog>",
97
+ "fieldKey": "<staticFieldKey verbatim>",
98
+ "filterExpression": "{{contains([\"<id-1>\",\"<id-2>\"];var(\"service_id\"))}}"
99
+ }
100
+ ```
101
+
102
+ **Shape A — scalar field** (`staticFieldKey` has no `[0]`):
103
+ `{{contains([<values>];var("<fieldKey>"))}}`
104
+
105
+ **Shape B — array-backed field** (`staticFieldKey` contains one `[0]`, e.g. `formIds[0]`, `forms[0].data.id`). `[0]` stands for "every item", not item zero. Split into `arrayPath` (before `[0]`) and `itemPath` (after `[0].`, may be empty):
106
+
107
+ - empty `itemPath`: `{{arraySome([<values>];contains(var("<arrayPath>");$_))}}`
108
+ - non-empty: `{{arraySome([<values>];contains(arrayMap(var("<arrayPath>");"<itemPath>");$_))}}`
109
+
110
+ MUST:
111
+
112
+ - `id` = the catalog filter `id`; `fieldKey` = `staticFieldKey` character for character, `[0]` included.
113
+ Read both from the trigger's full `filters[]`: Get Trigger By App Id And Key (§2 step 4), or a
114
+ Resolve Triggers result without `basicFieldsOnly`. A compact listing (`basicFieldsOnly`, a
115
+ `fields` projection, or your own local strip of the catalog) has no filter definitions; if your
116
+ copy lacks `filters[]`, fetch the trigger — never invent an `id` or `fieldKey`.
117
+ - Values are ALWAYS a literal array, even for one value (`["x"]`). Types match the field: strings quoted, numbers/booleans bare.
118
+ - `;` between arguments, double quotes only, no spaces, one non-empty expression per filter (an empty/missing `filterExpression` is a builder error), max 5 filters.
119
+ - String literals inside `{{…}}` (values and the `var` path) are URI-encoded like every bracket literal ([Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions) §1).
120
+ - Order `filters` as the catalog lists them, depth-first (each filter, then its follow-ups): the builder re-appends each filter in that order on panel open and compares deep-equal.
121
+
122
+ NEVER:
123
+
124
+ - `var("forms[0].id")` — validates, but checks only the first item.
125
+ - `contains(var("formIds");"x")` (value outside an array) — the builder cannot read it back.
126
+ - Any other function (`stringEq`, `arrayIncludesAnyOf`, `not`, …) in a filter — the filter panel only parses `contains` and `arraySome`.
127
+ - Filters on a field key with two array boundaries (`forms[0].ids[0]`), a literal index (`forms[1].id`) or a leaf that is an object/array rather than a string/number/boolean. There is no valid expression: configure the supported filters, tell the user this one is unavailable, and offer a condition step if the data is in the payload.
128
+
129
+ Updating an array-backed filter: change only the values array; keep the shape and `fieldKey`.
130
+
131
+ ### 4.3 Required, follow-up and dependent filters
132
+
133
+ - `valueInput.required: true` on a top-level filter → the filter MUST be saved. No user preference is not a reason to ask: pick a sensible default from the filter's own options (an "Any/All" option the trigger offers is fine); there is no wildcard expression. If it is also single-select (`multiSelect: false`), the trigger fires for exactly ONE chosen entity: a request for "any label/any service…" cannot be met with this trigger — offer: pick one, one automation per entity, or a different trigger. Check this during feasibility, before planning.
134
+ - A follow-up filter marked required is mandatory **only if its parent filter is set**. Setting a parent obliges you to set all its required follow-ups.
135
+ - NEVER save a follow-up without its parent: the builder hides it and deletes it when the panel opens. Only `ENTITY_SELECTOR` follow-ups of `ENTITY_SELECTOR` parents are shown at all.
136
+ - `entitySelector.queryFieldToFilterIdMapping` (`{"workflowId": "<parent-filter-id>"}`): options depend on the parent's value. Choose the parent first, then fetch child ids scoped to it (workflow → stage, program → step).
137
+ - `queryFieldToValueMapping` (`{"eventType": "RSVP"}`): options are pre-narrowed; pick ids from that subset, nothing extra in the expression.
138
+
139
+ Dependent example:
140
+
141
+ ```json
142
+ "filters": [
143
+ { "id": "<program-filter-id>", "fieldKey": "programId",
144
+ "filterExpression": "{{contains([\"<program-id>\"];var(\"programId\"))}}" },
145
+ { "id": "<step-filter-id>", "fieldKey": "programStepId",
146
+ "filterExpression": "{{contains([\"<step-id-1>\",\"<step-id-2>\"];var(\"programStepId\"))}}" }
147
+ ]
148
+ ```
149
+
150
+ ### 4.4 `DYNAMIC_FIELD_KEY` filters
151
+
152
+ The field to filter on is itself selected (`dynamicFieldKey.entitySelector`, dependent on a parent such as the form). Resolve the parent first, then:
153
+
154
+ - **The key** = the id of the field as that field-key selector lists it — the builder saves it as `fieldKey` and passes the same string to the value picker as its `fieldTarget`. For Wix Forms ("Option chosen on a form") that is the form field's **`target`** from the Forms API (`multi_choice_5734`) — not the dynamic-schema property key (`field:multi_choice_5734`, used only in `var()` paths of later steps) and not the field's uuid `id`.
155
+ - **The expression** is **Shape A** with that same key: `{{contains(["<value-1>"];var("<key>"))}}`. The builder reads the chosen key back from the `var(...)` and treats the filter as required; Shape B is never used here.
156
+ - **The values**: if `valueInput` is an `ENTITY_SELECTOR` scoped to that field (form choices), use the choice option's `value` from the Forms API (what a submission stores, e.g. `"Walter"`), quoted and URI-encoded (§4.2). The value picker's own ids aren't public, so this is a best match, and Validate checks neither key nor values: tell the user to open the trigger in the builder and confirm the field and choice show by name.
157
+ - Can't list the fields → ask the user.
158
+
159
+ ## 5. Dynamic-schema triggers — filters come first
160
+
161
+ When `implementedMethods.getDynamicSchema` is `true`:
162
+
163
+ 1. Decide the filter values, especially those with `valueInput.reevaluateDynamicSchema: true` (which form, which service).
164
+ 2. Call Get Trigger Dynamic Schema with what the builder sends: one entry per **saved** filter (`fieldKey` verbatim, its string values); if no filter is saved, each `reevaluateDynamicSchema` filter's `staticFieldKey` with `values: []`:
165
+ ```json
166
+ {
167
+ "appId": "<app-id>",
168
+ "triggerKey": "<trigger-key>",
169
+ "selectedFilterOptions": [
170
+ { "fieldKey": "formId", "values": ["<form-id>"] }
171
+ ]
172
+ }
173
+ ```
174
+ **Merge** the response into the static schema: take `payloadDataSchema` and overlay `dynamicSchema.properties` on its `properties` (dynamic wins on a key clash). The dynamic schema alone usually lacks static fields such as `contactId` — never use it by itself.
175
+ 3. Map and condition only on fields present in that schema. Labels differ from keys — copy keys from the schema.
176
+ 4. No filter chosen ⇒ entity-specific fields (form answers) are **unknown, not absent**, whatever the `values: []` call returns. Never map or condition on a form-answer field without the form filter: use only static fields (`contactId`, submission time, …) or ask which form, set that filter, then fetch the dynamic schema.
177
+
178
+ ## 6. Webhook and custom triggers
179
+
180
+ > **Later stages only:** not available in stage 2; this section is published in stage 3.
181
+
182
+ ## 7. Pre-save checklist
183
+
184
+ - [ ] `appId` + `triggerKey` came from Resolve Triggers on this site.
185
+ - [ ] Every filter `id` exists in the catalog trigger (or a follow-up); `fieldKey` is verbatim.
186
+ - [ ] Every expression is Shape A or Shape B (Shape A for dynamic field keys); values in a literal array; types per §4.1; ids, not names; no follow-up without its parent.
187
+ - [ ] Every `BOOLEAN` filter, and every `NUMBER` filter with a default, is saved.
188
+ - [ ] All required filters present; required follow-ups present for every set parent.
189
+ - [ ] For dynamic-schema triggers, downstream steps use the schema returned for the chosen filters.
190
+ - [ ] Final gate: Validate Automation returns no `triggerValidationErrors`.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: "Manage Site Collaborators"
3
+ description: Manages who has access to a Wix site as a collaborator (contributor) — finds the site's current collaborators, invites someone, removes a collaborator, and resends or revokes a pending collaborator invite. Use when the user wants to give someone access to the site, take it away, or follow up on an invitation that has not been accepted.
4
+ ---
5
+ # Manage Site Collaborators
6
+
7
+ ## Description
8
+ Changes who can work on a site: invite a collaborator, remove one, or resend / revoke an invitation that has not been accepted yet.
9
+
10
+ ## API Endpoints
11
+ Each call runs at one of two scopes. Use the scope shown. A site call is made with site scope and the site ID in the tool's site ID parameter; that is the only way the site reaches Wix, so never add a site ID to the request body. An account call is made with account scope, which ignores the site ID.
12
+
13
+ | Scope | Call | Purpose |
14
+ |---|---|---|
15
+ | Site | `GET https://www.wixapis.com/roles-management/v2/contributors/query` | list the site's collaborators |
16
+ | Site | `POST https://www.wixapis.com/roles-management/contributor/remove` | remove a collaborator |
17
+ | Account | `GET https://www.wixapis.com/roles-management/roles` | look up role IDs for invites |
18
+ | Site | `POST https://www.wixapis.com/invites/site-invite/bulk` | send invites (returns the `inviteId`s) |
19
+ | Site | `POST https://www.wixapis.com/invites/site-invite/{inviteId}/resend` | resend a pending invite |
20
+ | Site | `POST https://www.wixapis.com/invites/site-invite/{inviteId}/revoke` | revoke a pending invite |
21
+
22
+ Docs: [Contributors](https://dev.wix.com/docs/api-reference/account-level/user-management/sites/contributors/introduction), [Site Invites](https://dev.wix.com/docs/api-reference/account-level/user-management/sites/site-invites/introduction)
23
+
24
+ ## What the APIs can and cannot look up
25
+
26
+ - **This recipe has everything you need for every call below: endpoint, request and response shape. Do not search the docs, read the method articles or query the API spec before calling.** Go straight to the call. If the user names someone only by email or name, your final message must ask for their account ID (or invite ID for an invite) — say this even when the lookup shows only the owner.
27
+ - **Never call Remove Contributor, revoke or resend until you have the exact ID from the user or from an earlier response.** Look-ups by email or name are not possible, so ask first. If the user names someone only by email or name, read this recipe, then reply asking for the account ID (and how to find it) — at most one Query Site Contributors call, and do not search other APIs for an email-to-account lookup or try bulk or guessed deletes. Your final message must say that collaborators can only be identified by account ID, and ask the user to provide it — even if the query failed or returned nothing useful.
28
+ - **Collaborators are identified by account ID, not email.** Query Site Contributors returns only `accountId` and `accountOwnerId` per collaborator. If the user names a collaborator by email or name, ask them for that person's account ID, or for how to find it, before removing anyone. Do not guess which account ID belongs to which person.
29
+ - **Invites are identified by `inviteId`.** There is no endpoint to list a site's pending invites. The `inviteId` is returned when the invite is created (`invites[].id` in the Bulk Invite response, Step 6) — keep it if you will need to resend or revoke later in the conversation. If the user does not have it, say that it cannot be looked up through the API and ask them to provide it.
30
+ - **Get Roles is the only account-level call** (account scope). Query Site Contributors, Remove Contributor, Bulk Invite, resend and revoke are site-level: make them with site scope and the site ID. Never make Bulk Invite, resend or revoke with account scope: the site is dropped and Wix rejects the call with a missing-site error. If a call is still rejected (403), the cause is not always visible in the response; stop, tell the user the call was refused, and do not call the later steps of the flow.
31
+
32
+ ## Steps
33
+
34
+ ### 1. Establish which action the user wants and on whom
35
+
36
+ | User wants | Needs | Go to |
37
+ |---|---|---|
38
+ | Remove someone who already has access | their `accountId` | Step 2, then Step 3 |
39
+ | Remove themselves | their own `accountId` | Step 7 |
40
+ | Invite someone new | their email(s) and a role (`policyIds`) | Step 6 |
41
+ | Cancel an invite not yet accepted | the `inviteId` | Step 4 |
42
+ | Resend an invite not yet accepted | the `inviteId` | Step 5 |
43
+
44
+ Removal works only on existing collaborators; revoke and resend work only on pending invites. Do not use one for the other.
45
+
46
+ **When the user names the person only by email**, work out what that email is before acting:
47
+
48
+ 1. **Invited earlier in this conversation?** If the email matches an `invites[].email` from a Bulk Invite response you received, you have its `inviteId`: treat the person as an invitee (Step 4 or 5). If the user asked to *remove* them, cancel the invite (Step 4).
49
+ 2. **Otherwise the email cannot be classified.** The APIs cannot say whether it belongs to a collaborator, a pending invitee, or nobody, so do not claim "not a collaborator" or "has no access". Say that collaborators are identified by account ID and invites by invite ID, and ask for the one that fits the request (for removal, the account ID). Do not guess from the contributors list.
50
+ 3. **Wrong-mode guards.** Do not revoke or resend for someone the user says already has access, and do not remove someone the user says is only invited. In those cases tell the user which action applies instead (for example: "they have only been invited, so I can cancel the invite rather than remove them").
51
+
52
+ ### 2. List the site's collaborators
53
+
54
+ ```
55
+ GET https://www.wixapis.com/roles-management/v2/contributors/query
56
+ ```
57
+
58
+ No filter is needed to list everyone. To narrow by role, pass `filter.policyIds` (up to 20 role IDs) as repeated query parameters. Response:
59
+
60
+ ```json
61
+ { "contributors": [ { "accountId": "<account GUID>", "accountOwnerId": "<owner user GUID>" } ] }
62
+ ```
63
+
64
+ Always run this read-only query before removing anyone. Use it to confirm the `accountId` the user gave is a current collaborator. If it is not in the list, stop and tell the user that account is not a collaborator on the site, so there is nothing to remove. If it is the site owner's account (it matches `accountOwnerId`), do not remove it: an owner can leave only after transferring ownership to another user. Tell the user that.
65
+
66
+ ### 3. Remove a collaborator
67
+
68
+ Removal clears all of the collaborator's role assignments on the site, so they lose access. When the user gave the exact `accountId` and Step 2 shows it is a current collaborator (and not the owner), call it directly — do not ask for confirmation:
69
+
70
+ ```
71
+ POST https://www.wixapis.com/roles-management/contributor/remove
72
+ ```
73
+ ```json
74
+ { "accountId": "<accountId from step 2>" }
75
+ ```
76
+
77
+ The response is `{}` on success.
78
+
79
+ ### 4. Revoke a pending invite
80
+
81
+ When the user named the invite (or you created it earlier in this conversation and hold its `inviteId`), call it directly with an empty JSON body `{}`. Ask only if you cannot tell which invite is meant:
82
+
83
+ ```
84
+ POST https://www.wixapis.com/invites/site-invite/{inviteId}/revoke
85
+ ```
86
+
87
+ The response is `{}`. Revoke applies to pending invites only; if the person has since accepted and appears in the Step 2 list, use Step 3 instead.
88
+
89
+ ### 5. Resend a pending invite
90
+
91
+ When the user named the invite (or you created it earlier in this conversation and hold its `inviteId`), call it directly with an empty JSON body `{}`. Ask only if you cannot tell which invite is meant:
92
+
93
+ ```
94
+ POST https://www.wixapis.com/invites/site-invite/{inviteId}/resend
95
+ ```
96
+
97
+ The response contains the invite: `invite.id`, `invite.email`, `invite.status` (Pending, Used, Deleted, Declined, Expired) and `invite.expirationDate`. Report the status back to the user.
98
+
99
+ ### 6. Send an invite
100
+
101
+ Ask the user for the email address(es) (1–10) and which role to grant; `policyIds` are role IDs, not names. Look them up with `GET https://www.wixapis.com/roles-management/roles` ([Get Roles Info](https://dev.wix.com/docs/api-reference/account-level/user-management/accounts/users/get-roles-info)), which returns `predefinedRoles` and `customRoles`. Add `?filter.role_level=SITE_LEVEL` to list only site roles. Each entry is `{ "id": "<role id>", "title": "...", "description": "..." }`; use the ID of the role the user chose, and do not guess an ID. If the user said "any" or "a standard role", pick from `predefinedRoles` the role whose `description` grants the least access (view or content editing, not managing, billing or ownership) and name its `title` in your final message; if they gave no role at all, ask which one. Once you have the emails and a role, send the invite without asking for confirmation:
102
+
103
+ ```
104
+ POST https://www.wixapis.com/invites/site-invite/bulk
105
+ ```
106
+ ```json
107
+ { "policyIds": ["<role id>"], "emails": ["person@example.com"] }
108
+ ```
109
+
110
+ The response has `invites[]` (each with `id`, `email`, `status`, `expirationDate`) and `failedEmails` / `failedInvites`. Report any that failed. Keep each `invites[].id`: it is the `inviteId` for Steps 4 and 5.
111
+
112
+ ### 7. Remove the current user from the site ("remove me")
113
+
114
+ The APIs cannot tell who is talking to you, so you need the user's own `accountId` from them. Then:
115
+
116
+ 1. Run Step 2. If their account is not in the contributors list, stop: the site owner is not listed as a collaborator, and an owner can leave only after transferring ownership to another user. Tell them that and do not call Remove Contributor.
117
+ 2. Call Remove Contributor (Step 3) with their `accountId`. The user asked for this, so do not ask for confirmation, but do not soften it afterwards: removal cannot be undone.
118
+ 3. On success, state clearly and in bold that they can no longer use this site's dashboard, and should close the tab or click the Wix logo at the top left to go to All Sites. For example: **You have been removed from this site. The dashboard will no longer work for you — close this tab or click the Wix logo at the top left to go to All Sites.**
119
+
120
+ ## Reporting back
121
+
122
+ State what was done, to which account or email, and the outcome. Do not show raw IDs unless the user asks. Always say which of these happened:
123
+
124
+ - **Succeeded**, e.g. "The invite for james@bond.com has been canceled."
125
+ - **Failed**, e.g. "I couldn't resend the invite for james@bond.com. Please try again." If the call returns 403, say so; do not guess a cause.
126
+ - **Not done**, with the reason, e.g. "That account is not a collaborator on this site, so there was nothing to remove." or "That is the site owner's account, which cannot be removed without transferring ownership."
127
+
128
+ For resend, read `invite.status` in the response: if it is `Used`, the person already accepted and is now a collaborator, so say they already have access rather than that the invite was resent; `Declined`, `Expired` or `Deleted` mean the invite is no longer pending.
@@ -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 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."
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 the dashboard link for each Forms entity; reading forms and submissions is Read Forms and Submissions."
4
4
  ---
5
5
 
6
6
  # Forms Dashboard Navigation
@@ -33,6 +33,8 @@ Fetch the entity via REST, then link the matching dashboard page. All calls use
33
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) |
34
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 |
35
35
 
36
+ For complete read requests — the namespace on each call, the submission fields you can filter and sort by, paging — see [Read Forms and Submissions](./read-forms-and-submissions.md).
37
+
36
38
  Example — after creating a form, hand back its links:
37
39
 
38
40
  ```
@@ -0,0 +1,277 @@
1
+ ---
2
+ name: "Read Forms and Submissions"
3
+ description: "Reads a site's Wix forms and the submissions visitors sent to them — what forms do I have, find a form by name, show the latest submissions, who filled in my contact form, how many submissions did I get last week, submissions from one contact, unread submissions. Covers finding the form's namespace instead of guessing it, where that namespace goes on each read, the Form Schemas and Form Submissions endpoints (and the look-alike URLs that 404), forms hidden from a default listing, the closed list of submission fields you can filter and sort by, date ranges, cursor paging, totals per form, and reading answers by field. Read-only; changing a form is Update Form, building one is Create Form."
4
+ ---
5
+ # RECIPE: Read Wix Forms and Their Submissions
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
+ > These calls need the Wix Forms app on the site (`appDefId` `225dd912-7dea-4738-8688-4b8c6955ffc2`); a `428`, a `400` naming the app, or an empty result on a site that should have content means it is not there. [List Installed Apps](../app-installation/list-installed-apps.md) shows what the site has; [Install Wix Apps](../app-installation/install-wix-apps.md) adds it by that id.
10
+
11
+ List a site's forms, find one, and read the submissions sent to it. The usual order is **find the form first, then query its submissions by `formId`**. Every call here is a read — nothing on the site changes, so no confirmation is needed.
12
+
13
+ ---
14
+
15
+ ## STEP 1 · Know the namespace — never guess it
16
+
17
+ Every form and every submission belongs to one **namespace**: the app that owns the form. Every read except Get Form and Get Submission needs it.
18
+
19
+ | App that owns the form | Namespace |
20
+ |---|---|
21
+ | Wix Forms (the forms on the site's **Forms** dashboard) | `wix.form_app.form` |
22
+ | Wix Bookings | `wix.bookings.v2.bookings` |
23
+ | Wix Pricing Plans | `wix.pricing_plans.v1.custom_form` |
24
+ | Wix eCom checkout | `wix.ecom.v1.checkout` |
25
+ | Wix Quizzes | `wix.achievements.quizzes.v1.quiz` |
26
+ | Wix Contacts | `wix.contacts.v4.contact` |
27
+
28
+ The source is [Namespaces of apps made by Wix](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/introduction#namespaces-of-apps-made-by-wix).
29
+
30
+ - **"My forms", "my contact form", "form submissions"** with no app named → `wix.form_app.form`.
31
+ - **You already hold a form** → use its `namespace` field.
32
+ - **`Permissions for given namespace not found: <namespace>`** → that app is not installed on the site. **Stop.** Check the site's apps with [List Installed Apps](../app-installation/list-installed-apps.md) and tell the user the app is missing. Do not try other namespaces (`wix.old_form_app.form`, `wix.forms`, …): none of them exist, and each one only costs another failed call.
33
+ - **List Forms returns `"forms": []`** for a namespace → it means *no forms there*, or *the wrong namespace*. It does not error on a namespace the site lacks.
34
+
35
+ ---
36
+
37
+ ## STEP 2 · Pick the call
38
+
39
+ | Task | Request | Where the namespace goes |
40
+ |---|---|---|
41
+ | List forms | `GET https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form` | query parameter `namespace` (required) |
42
+ | Query forms (by name, date) | `POST https://www.wixapis.com/form-schema-service/v4/forms/query` | inside the filter: `"namespace": {"$eq": "wix.form_app.form"}` |
43
+ | Get one form | `GET https://www.wixapis.com/form-schema-service/v4/forms/{formId}` | not needed |
44
+ | Query submissions | `POST https://www.wixapis.com/form-submission-service/v4/submissions/namespace/query` | inside the filter: `"namespace": "wix.form_app.form"` |
45
+ | Get one submission | `GET https://www.wixapis.com/form-submission-service/v4/submissions/{submissionId}` | not needed |
46
+ | Count submissions per form | `POST https://www.wixapis.com/form-submission-service/v4/submissions/count` | body field `namespace` |
47
+
48
+ **⚠️ These look-alike URLs do not exist and return a bare `404`:**
49
+
50
+ - `/forms/v4/forms`, `/forms/v4/forms/query`, `/forms/v4/forms/{formId}` — forms live under **`/form-schema-service/v4/`**.
51
+ - `/form-schema-service/v4/submissions/...` and `/form-submission-service/v4/forms` — submissions live under **`/form-submission-service/v4/submissions/`**, forms don't.
52
+
53
+ **Use `.../submissions/namespace/query` for every submissions query.** It is the only submissions query the docs list. Don't drop `namespace` from the path.
54
+
55
+ `https://www.wixapis.com/forms/v4/submissions/...` (the `publicUrl` the API spec gives for the submission methods) is the same API under another path, and works too. The `/forms/v4/` alias exists for submissions only: `/forms/v4/forms` still 404s.
56
+
57
+ ---
58
+
59
+ ## STEP 3 · List or find the forms
60
+
61
+ ### List every form
62
+
63
+ ```bash
64
+ # enabled forms (the default)
65
+ curl -X GET \
66
+ 'https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&fieldsets=METADATA&paging.limit=100' \
67
+ -H 'Authorization: <AUTH>'
68
+
69
+ # disabled forms — a second call, always
70
+ curl -X GET \
71
+ 'https://www.wixapis.com/form-schema-service/v4/forms?namespace=wix.form_app.form&enabled=false&fieldsets=METADATA&paging.limit=100' \
72
+ -H 'Authorization: <AUTH>'
73
+ ```
74
+
75
+ - **⚠️ `enabled` defaults to `true`, and no value returns both.** One call hides every disabled form. With List Forms, make both calls and report both lists, marking the disabled ones. Or use Query Forms with only the namespace filter (below): it returns enabled and disabled forms in one call.
76
+ - **`fieldsets=METADATA`** returns only the top-level properties (`id`, `name`, `enabled`, dates, `namespace`) and leaves out fields, steps and rules. Use it whenever you only need names and IDs. Omit it when you need the fields (to read submission answers, see STEP 5).
77
+ - Response: `forms[]` and **`pagingMetadata`**. When `pagingMetadata.hasNext` is `true`, call again with `paging.cursor=<pagingMetadata.cursors.next>`.
78
+ - To read specific forms, add `formIds=<id>` (repeat the parameter for each ID).
79
+
80
+ ### Find a form by name or date
81
+
82
+ ```bash
83
+ curl -X POST \
84
+ 'https://www.wixapis.com/form-schema-service/v4/forms/query' \
85
+ -H 'Content-Type: application/json' \
86
+ -H 'Authorization: <AUTH>' \
87
+ -d '{
88
+ "query": {
89
+ "filter": {
90
+ "namespace": { "$eq": "wix.form_app.form" },
91
+ "name": { "$startsWith": "Contact" }
92
+ },
93
+ "sort": [{ "fieldName": "createdDate", "order": "DESC" }],
94
+ "cursorPaging": { "limit": 50 }
95
+ }
96
+ }'
97
+ ```
98
+
99
+ - Filter and sort fields: `id`, `namespace` (`$eq` only), `name` (`$eq`, `$ne`, `$in`, `$nin`, `$startsWith`), `enabled` (`$eq`, `$ne`), `createdDate` and `updatedDate` (`$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`). Date operators are `$gte` / `$lte`. **`$ge` / `$le` are rejected.** A range needs one condition per operator under `$and`, exactly as for submissions in STEP 4.
100
+ - Query Forms returns enabled and disabled forms together, unless you filter on `enabled`.
101
+ - Response: `forms[]` and **`metadata`** (not `pagingMetadata`). For the next page, see [Paging](#paging).
102
+
103
+ ### Read one form
104
+
105
+ ```bash
106
+ curl -X GET \
107
+ 'https://www.wixapis.com/form-schema-service/v4/forms/{formId}' \
108
+ -H 'Authorization: <AUTH>'
109
+ ```
110
+
111
+ Returns `form`, with `formFields` (each input field's `inputOptions.target` is the key its answers are stored under) and `namespace`.
112
+
113
+ ---
114
+
115
+ ## STEP 4 · Query the submissions
116
+
117
+ ### The latest submissions to one form
118
+
119
+ ```bash
120
+ curl -X POST \
121
+ 'https://www.wixapis.com/form-submission-service/v4/submissions/namespace/query' \
122
+ -H 'Content-Type: application/json' \
123
+ -H 'Authorization: <AUTH>' \
124
+ -d '{
125
+ "query": {
126
+ "filter": {
127
+ "namespace": "wix.form_app.form",
128
+ "formId": "<formId>"
129
+ },
130
+ "sort": [{ "fieldName": "createdDate", "order": "DESC" }],
131
+ "cursorPaging": { "limit": 20 }
132
+ }
133
+ }'
134
+ ```
135
+
136
+ - **⚠️ `namespace` goes inside `query.filter`.** A top-level `"namespace"` beside `query` doesn't count: the call fails with `namespace filter is required`.
137
+ - Leave out `formId` to read submissions across every form in the namespace.
138
+ - Response: `submissions[]` and **`metadata`** (`count`, `hasNext`, `cursors.next`). Each submission has `id`, `formId`, `status`, `seen`, `contactId` (when linked to a contact), `submitter`, `createdDate`, and `submissions` (the answers, see STEP 5).
139
+
140
+ ### Read one submission
141
+
142
+ When you already hold a submission ID, read it directly. No namespace is needed:
143
+
144
+ ```bash
145
+ curl -X GET \
146
+ 'https://www.wixapis.com/form-submission-service/v4/submissions/{submissionId}' \
147
+ -H 'Authorization: <AUTH>'
148
+ ```
149
+
150
+ Returns `submission`, with the same fields as a query result, including `formId` (the form it belongs to) and the answers in `submissions`.
151
+
152
+ ### When it was submitted = `createdDate`
153
+
154
+ **The submission time is `createdDate`.** Filter and sort on it. These fields **do not exist**, and each one fails with `Filter contains unsupported condition` or `Sort contains unsupported condition`: `submittedDate`, `submittedAt`, `submissionTime`, `submissionDate`, `dateCreated`, `_createdDate`.
155
+
156
+ ### A date range — one condition per operator, joined with `$and`
157
+
158
+ A field takes **one** operator. Two operators in one object, such as `"createdDate": {"$gte": …, "$lt": …}`, fail with `Field '$lt' is not supported for filter with operator '$eq'`. Put each bound in its own condition under `$and`:
159
+
160
+ ```bash
161
+ curl -X POST \
162
+ 'https://www.wixapis.com/form-submission-service/v4/submissions/namespace/query' \
163
+ -H 'Content-Type: application/json' \
164
+ -H 'Authorization: <AUTH>' \
165
+ -d '{
166
+ "query": {
167
+ "filter": {
168
+ "$and": [
169
+ { "namespace": "wix.form_app.form" },
170
+ { "createdDate": { "$gte": "2026-10-05T00:00:00Z" } },
171
+ { "createdDate": { "$lt": "2026-10-12T00:00:00Z" } }
172
+ ]
173
+ },
174
+ "sort": [{ "fieldName": "createdDate", "order": "DESC" }],
175
+ "cursorPaging": { "limit": 100 }
176
+ }
177
+ }'
178
+ ```
179
+
180
+ - Write dates as ISO 8601 UTC. For "last week", work out the week's start and end from today's date, and say which dates you used.
181
+ - **"How many…" in a date range**: follow every page and add up `metadata.count`. There is no total in the response, and Count Submissions takes no date filter.
182
+ - **"How many…" in total, per form**: Count Submissions.
183
+
184
+ ```bash
185
+ curl -X POST \
186
+ 'https://www.wixapis.com/form-submission-service/v4/submissions/count' \
187
+ -H 'Content-Type: application/json' \
188
+ -H 'Authorization: <AUTH>' \
189
+ -d '{ "formIds": ["<formId>"], "namespace": "wix.form_app.form" }'
190
+ ```
191
+
192
+ Returns `formsSubmissionsCount[]`, with `formId`, `totalCount` and `unseenCount` for each form.
193
+
194
+ ### The fields you can filter and sort by — a closed list
195
+
196
+ | Field | Filter operators | Sortable |
197
+ |---|---|---|
198
+ | `namespace` | `$eq` — **required in every filter** | no |
199
+ | `id`, `formId`, `contactId`, `status` | `$eq`, `$ne`, `$in`, `$nin` | yes |
200
+ | `seen` | `$eq`, `$ne` | yes |
201
+ | `createdDate`, `updatedDate` | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin` | yes |
202
+ | `submitter.memberId`, `submitter.visitorId`, `submitter.applicationId`, `submitter.userId` | `$eq`, `$ne`, `$in`, `$nin` | yes |
203
+ | `orderDetails.checkoutId` | `$eq`, `$ne`, `$in`, `$nin` | yes |
204
+
205
+ Any other field is rejected. The common wrong ones:
206
+
207
+ | You want | Use | Not |
208
+ |---|---|---|
209
+ | Submissions from one contact | `"contactId": "<contactId>"` (top level) | `submitter.contactId`, `submitterContactId` |
210
+ | Submissions to one form | `"formId": "<formId>"` | `submission.formId` |
211
+ | Unread submissions | `"seen": false` | — |
212
+ | Recorded submissions only | `"status": "CONFIRMED"` | — |
213
+ | Submissions with a given answer (an email typed in, a choice picked) | Not filterable. Filter by `formId`, then match the answer in code (STEP 5). | `submissions.email`, `submissions.<target>`, `contact.email` |
214
+
215
+ To find a person's submissions by their email, look up the contact first, then filter submissions by `"contactId": "<contacts[0].id>"`:
216
+
217
+ ```bash
218
+ curl -X POST \
219
+ 'https://www.wixapis.com/contacts/v4/contacts/query' \
220
+ -H 'Content-Type: application/json' \
221
+ -H 'Authorization: <AUTH>' \
222
+ -d '{ "query": { "filter": { "info.emails.email": "jane@example.com" }, "fieldsets": ["BASIC"] } }'
223
+ ```
224
+
225
+ No contact found means no submissions linked to that email. See [Query Contacts](https://dev.wix.com/docs/api-reference/crm/members-contacts/contacts/contacts/contact-v4/query-contacts).
226
+
227
+ ### Paging
228
+
229
+ Each page holds at most **100** submissions or forms. When `metadata.hasNext` is `true`, send the same URL with **only** the cursor. The cursor carries the filter and sort, including the namespace:
230
+
231
+ ```json
232
+ { "query": { "cursorPaging": { "limit": 100, "cursor": "<metadata.cursors.next>" } } }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## STEP 5 · Read the answers
238
+
239
+ A submission's answers are in its `submissions` object, keyed by each field's **`target`**:
240
+
241
+ ```json
242
+ "submissions": { "email_1923": "jane@example.com", "short_answer_7b5c": "Hello" }
243
+ ```
244
+
245
+ The targets are not labels. To show the user readable answers, read the form once (Get Form) and match each `formFields[].inputOptions.target` to the label inside that field's `inputOptions`, for example `stringOptions.textInputOptions.label`.
246
+
247
+ **Return only what the user asked for.** A submission page can be large. Before you answer or pass results on, keep only the fields you need, such as `id`, `createdDate`, `contactId` and the few answers that matter. Use `fieldsets=METADATA` on List Forms for the same reason.
248
+
249
+ To give the user a link to a form's submissions table, see [Forms Dashboard Navigation](./forms-dashboard-navigation.md).
250
+
251
+ ---
252
+
253
+ ## Troubleshooting
254
+
255
+ | Error / symptom | Cause | Fix |
256
+ |---|---|---|
257
+ | `namespace must not be empty` / `namespace has size 0, expected 10 or more` on List Forms | The `namespace` query parameter is missing | Add `?namespace=wix.form_app.form` |
258
+ | `namespace filter is required` on a submissions query | `namespace` is missing from `query.filter`, or sent at the top level | Put `"namespace": "wix.form_app.form"` inside `query.filter` |
259
+ | `Permissions for given namespace not found: …` | The site doesn't have that app | Stop. Check [installed apps](../app-installation/list-installed-apps.md) and tell the user. Don't try other namespaces |
260
+ | A bare `404` with no message | An invented URL such as `/forms/v4/forms` or `/form-schema-service/v4/submissions/...` | Use the URLs in STEP 2 exactly |
261
+ | `Sort contains unsupported condition` / `Filter contains unsupported condition` | A field outside the closed list, such as `submittedDate`, `_createdDate`, `submitter.contactId` or an answer key | Use `createdDate`, `contactId`, or the table in STEP 4 |
262
+ | `Field '$lt' is not supported for filter with operator '$eq'` | Two operators in one field object | One condition per operator under `$and` |
263
+ | `Filter contains unsupported operator` on Query Forms | Two operators in one field object | One condition per operator under `$and` |
264
+ | `Field 'createdDate' is using non allowed operator '$ge'` | `$ge` / `$le` | Use `$gte` / `$lte` |
265
+ | The user says a form is missing from the list | List Forms defaults to `enabled=true` | Call again with `enabled=false` |
266
+ | `"forms": []` | Wrong namespace, no forms there, or (on List Forms) every form is disabled | Check the namespace (STEP 1) and call again with `enabled=false` before reporting "no forms" |
267
+
268
+ ---
269
+
270
+ ## Related Documentation
271
+
272
+ - [Update Form](./update-form.md) and [Create Form](./create-form.md) — changing or building a form
273
+ - [Forms Dashboard Navigation](./forms-dashboard-navigation.md) — links to a form and its submissions table
274
+ - [List Forms](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/list-forms) · [Query Forms](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/query-forms) · [Get Form](https://dev.wix.com/docs/api-reference/crm/forms/form-schemas/get-form)
275
+ - [Query Submissions By Namespace](https://dev.wix.com/docs/api-reference/crm/forms/form-submissions/query-submissions-by-namespace) · [Count Submissions](https://dev.wix.com/docs/api-reference/crm/forms/form-submissions/count-submissions)
276
+ - [About Submission Values](https://dev.wix.com/docs/api-reference/crm/forms/form-submissions/about-submission-values) — what each field type stores
277
+ - [About the Wix API Query Language](https://dev.wix.com/docs/api-reference/articles/work-with-wix-apis/data-retrieval/about-the-wix-api-query-language) — `$and`, `$or`, paging