@wix/agent-skills 1.16.2 → 1.17.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.
- package/package.json +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +134 -120
- package/skills/wix-app/references/EMBEDDED_SCRIPT.md +6 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +2 -2
- package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +2 -2
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +182 -99
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +92 -47
- package/skills/wix-app/references/editor-react-component/BRANDED-COMPONENTS.md +15 -6
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +275 -0
- package/skills/wix-app/references/editor-react-component/COMPONENT-PREVIEW.md +29 -42
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +153 -449
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +66 -19
- package/skills/wix-app/references/editor-react-component/DIRECTIONALITY.md +53 -158
- package/skills/wix-app/references/editor-react-component/EDITOR-EXTENSION-CONFIGURATION.md +78 -0
- package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +42 -134
- package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +27 -0
- package/skills/wix-app/references/editor-react-component/PARTS.md +45 -24
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +63 -179
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +79 -163
- package/skills/wix-app/references/editor-react-component/SITE-CONTEXT-HOOKS.md +36 -21
- package/skills/wix-app/references/editor-react-component/SSR.md +30 -99
- package/skills/wix-base44-connector/SKILL.md +34 -17
- package/skills/wix-base44-connector/scripts/utils.js +33 -25
- package/skills/wix-headless/entry/bootstrap.mjs +9 -2
- package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +42 -23
- package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +10 -8
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +27 -24
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +5 -0
- package/skills/wix-manage/SKILL.md +14 -2
- package/skills/wix-manage/references/bookings/booking-system-integration-gaps.md +20 -13
- package/skills/wix-manage/references/bookings/create-class-service.md +1 -1
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +24 -11
- package/skills/wix-manage/references/cms/cms-ecommerce-catalog-integration.md +5 -5
- package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +182 -5
- package/skills/wix-manage/references/seo/manage-seo-tags.md +315 -0
- package/skills/wix-manage/references/seo/manage-url-redirects.md +250 -0
- package/skills/wix-manage/references/sites/query-sites.md +72 -161
- package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +5 -6
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +13 -9
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +10 -7
- package/skills/wix-vibe-headless/platforms/base44.md +13 -10
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +2 -0
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-checkout.js +9 -8
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +4 -1
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +23 -11
- package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +1 -1
- package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +29 -31
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +15 -8
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +37 -38
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +2 -2
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +35 -35
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW-REFERENCE.md +0 -172
- package/skills/wix-app/references/editor-react-component/A11Y-REVIEW.md +0 -143
- package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +0 -484
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +0 -225
- package/skills/wix-app/references/editor-react-component/EDIT-FLOW.md +0 -6
- package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +0 -95
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Manage a Wix Site's SEO Tags"
|
|
3
|
+
description: Read and update the SEO tags of a Wix site at the right level — site-wide tags, page-type patterns, or one item's tags. Discover item IDs and pattern variables instead of inventing them, read before every full-replace write, and report resolved tags with the source each one came from.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Manage a Wix Site's SEO Tags
|
|
7
|
+
|
|
8
|
+
Use the public **SEO APIs** to read and write the titles, descriptions, social
|
|
9
|
+
share tags, canonical links, structured data, and indexing directives of the
|
|
10
|
+
authenticated Wix site. The API selects the site from the caller's
|
|
11
|
+
authorization context; never ask for or send a site ID.
|
|
12
|
+
|
|
13
|
+
Writing tags requires the **Manage SEO Settings** permission. Reading tags or
|
|
14
|
+
listing pattern variables does not prove that the caller can write.
|
|
15
|
+
|
|
16
|
+
## The one rule that must never be skipped
|
|
17
|
+
|
|
18
|
+
**Every write replaces the target's tags in full, so a Get always immediately
|
|
19
|
+
precedes a Set.** There is no partial update: sending only the tag the user
|
|
20
|
+
asked about deletes every other tag that item, pattern, or site had. So before
|
|
21
|
+
any Set, call the matching Get for that exact target, merge the requested change
|
|
22
|
+
into the tags it returns, and send the complete set back.
|
|
23
|
+
|
|
24
|
+
Never write from the user's request alone, and never skip the Get because the
|
|
25
|
+
change looks small, because a list call already returned something, or because
|
|
26
|
+
the target looks empty. The Set response returns the updated tags and
|
|
27
|
+
`resolvedTags`, so report the outcome from that response instead of issuing
|
|
28
|
+
another read.
|
|
29
|
+
|
|
30
|
+
## Choose the level
|
|
31
|
+
|
|
32
|
+
Wix combines tags from several sources, where a more specific source wins.
|
|
33
|
+
Match the user's request to the level that owns the change:
|
|
34
|
+
|
|
35
|
+
| User intent | Level | API |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| "Change my site's default social image", site verification tags, site-wide indexing | Site | **Site SEO Tags** |
|
|
38
|
+
| "All my product/blog/event pages should be titled like X" — a convention for every item of a page type | Pattern | **SEO Patterns** |
|
|
39
|
+
| "Change the title of this page/product/post" — one specific item | Item | **Item SEO Tags** |
|
|
40
|
+
|
|
41
|
+
Work at the level that matches the change. Writing the same title onto many
|
|
42
|
+
items is the same outcome as one pattern and much harder to undo. If the user's
|
|
43
|
+
words fit more than one level, ask one short question before writing. An
|
|
44
|
+
explicit request to change a specific tag at a clear level is already
|
|
45
|
+
confirmation to make that write.
|
|
46
|
+
|
|
47
|
+
All three APIs live under the **SEO** category of the Wix REST API reference.
|
|
48
|
+
The request shapes are in the section below — construct requests from them. The
|
|
49
|
+
reference article URLs follow this pattern (for edge cases not covered here):
|
|
50
|
+
|
|
51
|
+
`https://dev.wix.com/docs/api-reference/business-management/seo/{api}/` + method slug
|
|
52
|
+
|
|
53
|
+
| Level | API slug | Methods (slug) |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Site | `site-seo-tags-v1` | Get Site SEO Tags (`get-site-seo-tags`), Set Site SEO Tags (`set-site-seo-tags`) |
|
|
56
|
+
| Pattern | `seo-pattern-v1` | Get SEO Pattern (`get-seo-pattern`), List SEO Patterns (`list-seo-patterns`), List SEO Pattern Variables (`list-seo-pattern-variables`), Create SEO Pattern (`create-seo-pattern`), Set SEO Pattern (`set-seo-pattern`), Reset SEO Pattern To Default (`reset-seo-pattern-to-default`) |
|
|
57
|
+
| Item | `item-seo-tags-v1` | Get Item SEO Tags (`get-item-seo-tags`), List Item SEO Tags (`list-item-seo-tags`), Set Item SEO Tags (`set-item-seo-tags`), Bulk Set Item SEO Tags (`bulk-set-item-seo-tags`), Reset Item SEO Tags To Default (`reset-item-seo-tags-to-default`) |
|
|
58
|
+
|
|
59
|
+
## REST request and response shapes
|
|
60
|
+
|
|
61
|
+
Use these shapes directly — do not go looking for them in the docs first.
|
|
62
|
+
|
|
63
|
+
Build requests from the shapes below. Do not search the API schemas or read
|
|
64
|
+
the reference article to construct them.
|
|
65
|
+
|
|
66
|
+
### Set Item SEO Tags — `PATCH /item-seo-tags/{itemType}/{itemId}`
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"itemSeoTags": {
|
|
71
|
+
"tags": [
|
|
72
|
+
{ "type": "title", "children": "Page title here" },
|
|
73
|
+
{ "type": "meta", "props": { "name": "description", "content": "Description here" } }
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"fieldMask": "tags"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Get Item SEO Tags — `GET /item-seo-tags/{itemType}/{itemId}`
|
|
81
|
+
|
|
82
|
+
No request body. Returns `itemSeoTags` with `tags`, `resolvedTags`,
|
|
83
|
+
`hasOverride`, `publishStatus`, and `hostPageId`.
|
|
84
|
+
|
|
85
|
+
### List Item SEO Tags — `GET /item-seo-tags/{itemType}?paging.limit=100`
|
|
86
|
+
|
|
87
|
+
No request body. Returns `itemSeoTagsList[]` and `pagingMetadata` with cursors.
|
|
88
|
+
|
|
89
|
+
### Set Site SEO Tags — `PATCH /site-seo-tags`
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"siteSeoTags": {
|
|
94
|
+
"tags": [
|
|
95
|
+
{ "type": "meta", "props": { "name": "google-site-verification", "content": "token" } }
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
"fieldMask": "tags"
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Set SEO Pattern — `PATCH /seo-patterns/{pageType}`
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"seoPattern": {
|
|
107
|
+
"pattern": {
|
|
108
|
+
"tags": [
|
|
109
|
+
{ "type": "title", "children": "{{item.name}} | {{site.name}}" }
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"fieldMask": "pattern"
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Common rules across all shapes
|
|
118
|
+
|
|
119
|
+
A tag is `{type, props, children}`: `title` and `script` carry their text in
|
|
120
|
+
`children`; `meta` and `link` carry theirs in `props` (`name`/`content` for a
|
|
121
|
+
meta tag, `rel`/`href` for a link).
|
|
122
|
+
|
|
123
|
+
`fieldMask` over REST is a comma-separated **string** (`"fieldMask": "tags"`);
|
|
124
|
+
the SDK takes an **array** (`fieldMask: ["tags"]`). Sending an array to REST
|
|
125
|
+
returns `INVALID_FIELD_MASK`. `publish` is a boolean.
|
|
126
|
+
|
|
127
|
+
`resolvedTags` in any Get or Set response is an array of
|
|
128
|
+
`{tag, source, inheritedTag}`. `source` is one of `TAG_SOURCE_SITE`,
|
|
129
|
+
`TAG_SOURCE_DEFAULT_PATTERN`, `TAG_SOURCE_USER_PATTERN`, `TAG_SOURCE_HOST_PAGE`,
|
|
130
|
+
`TAG_SOURCE_ITEM`, or `TAG_SOURCE_UNSPECIFIED`. `inheritedTag` appears only when
|
|
131
|
+
this source replaced a value a lower source had set.
|
|
132
|
+
|
|
133
|
+
All paths are relative to `https://www.wixapis.com/promote/seo/v1`.
|
|
134
|
+
|
|
135
|
+
## Discover, never invent
|
|
136
|
+
|
|
137
|
+
- An item is addressed by an **item type and item ID together**, not by URL.
|
|
138
|
+
Common item types: `STATIC_PAGE`, `STORES_PRODUCT`, `STORES_CATEGORY`,
|
|
139
|
+
`BLOG_POST`, `BLOG_CATEGORY`, `BOOKINGS_SERVICE`, `EVENTS_PAGE`,
|
|
140
|
+
`PORTFOLIO_PROJECTS`, `PORTFOLIO_COLLECTIONS`, `RESTAURANTS_MENU_PAGE`.
|
|
141
|
+
- To find an item's ID when the user refers to it by name (e.g. a product
|
|
142
|
+
name), use Search Products v3: `POST /stores/v3/products/search`
|
|
143
|
+
to get the product ID, then use that ID as the `itemId` with item type
|
|
144
|
+
`STORES_PRODUCT`. Do not call List Item SEO Tags and scan through all items
|
|
145
|
+
to match by name — use the vertical API.
|
|
146
|
+
- Never fabricate an item ID. If you cannot find the item through its vertical
|
|
147
|
+
API or List Item SEO Tags, ask the user.
|
|
148
|
+
- Item types exist on a site only when the business solution providing them is
|
|
149
|
+
installed. The authoritative list of supported item types is in the
|
|
150
|
+
`UNSUPPORTED_ITEM_TYPE` error message; do not hardcode one beyond the common
|
|
151
|
+
values listed above.
|
|
152
|
+
- Before writing a pattern, call **List SEO Pattern Variables** for the page
|
|
153
|
+
type and build the pattern only from returned variables. Never invent a
|
|
154
|
+
variable name.
|
|
155
|
+
- Dynamic Wix Data pages are addressed by `pageId`; addressing a pattern by
|
|
156
|
+
collection name is not supported.
|
|
157
|
+
|
|
158
|
+
## The write sequence
|
|
159
|
+
|
|
160
|
+
Exactly three calls, in this order, with no extra probing:
|
|
161
|
+
|
|
162
|
+
1. **Get** the current tags or pattern for the target. Required — see the rule
|
|
163
|
+
above.
|
|
164
|
+
2. **Set** the complete merged set, naming only the changed fields in the field
|
|
165
|
+
mask.
|
|
166
|
+
3. Report from the **Set response**, which returns the updated tags and
|
|
167
|
+
`resolvedTags`. Do not issue a verification read: the write response is the
|
|
168
|
+
confirmation. Read again only to check a static page's published revision
|
|
169
|
+
after `publish: true`, or when the user asks about a target you have not read
|
|
170
|
+
in this session.
|
|
171
|
+
|
|
172
|
+
To remove an item's own tags so it inherits again, call **Reset Item SEO Tags To
|
|
173
|
+
Default** (or **Reset SEO Pattern To Default**) — never set an empty list to
|
|
174
|
+
mean "reset".
|
|
175
|
+
|
|
176
|
+
## Worked example: set a product's SEO title and description
|
|
177
|
+
|
|
178
|
+
The user asks: *"Set the SEO title of my product 'Handmade Mug' to 'Handmade
|
|
179
|
+
Ceramic Mug | Studio Shop' and its description to 'A stoneware mug.'"*
|
|
180
|
+
|
|
181
|
+
**Step 1 — find the product ID.** Search Products v3 by name:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
POST https://www.wixapis.com/stores/v3/products/search
|
|
185
|
+
{ "search": { "search": { "expression": "Handmade Mug" } } }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{ "products": [{ "id": "a1b2c3d4-...", "name": "Handmade Mug", ... }] }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Take `products[0].id`. The item type is `STORES_PRODUCT`. Do not use Query
|
|
193
|
+
Products with a `name` filter — `name` is not filterable and returns 400.
|
|
194
|
+
|
|
195
|
+
**Step 2 — read the current tags.**
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
GET https://www.wixapis.com/promote/seo/v1/item-seo-tags/STORES_PRODUCT/{productId}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"itemSeoTags": {
|
|
204
|
+
"tags": [],
|
|
205
|
+
"hasOverride": false,
|
|
206
|
+
"resolvedTags": [
|
|
207
|
+
{ "tag": { "type": "title", "children": "Handmade Mug | My Site" },
|
|
208
|
+
"source": "TAG_SOURCE_DEFAULT_PATTERN" }
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`tags` is empty because the item has no overrides yet. `resolvedTags` shows
|
|
215
|
+
what the page currently renders with and where each tag comes from.
|
|
216
|
+
|
|
217
|
+
**Step 3 — merge and write.** Take the full `tags` array from step 2, replace
|
|
218
|
+
or add the title and description, and send the complete set back:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
PATCH /item-seo-tags/STORES_PRODUCT/{productId}
|
|
222
|
+
{
|
|
223
|
+
"itemSeoTags": {
|
|
224
|
+
"tags": [
|
|
225
|
+
{ "type": "title", "children": "Handmade Ceramic Mug | Studio Shop" },
|
|
226
|
+
{ "type": "meta", "props": { "name": "description", "content": "A stoneware mug." } }
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
"fieldMask": "tags"
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Step 4 — report from the response.** The Set response returns the updated
|
|
234
|
+
`tags` and `resolvedTags`. Report each resolved tag with its source. Do not
|
|
235
|
+
issue another Get — the write response is the confirmation.
|
|
236
|
+
|
|
237
|
+
## Common agent mistakes — do not make these
|
|
238
|
+
|
|
239
|
+
- **Searching the API schemas or reading the reference article before the first
|
|
240
|
+
call.** The request shapes are in this recipe. If you get a 400, compare your
|
|
241
|
+
request against the shapes in this recipe — do not go to the docs.
|
|
242
|
+
- **Searching for the SEO endpoints through API spec tools.** The shapes are
|
|
243
|
+
above; searching wastes a turn.
|
|
244
|
+
- **Sending `fieldMask` as an array over REST.** Over REST it is a
|
|
245
|
+
comma-separated string: `"fieldMask": "tags"`. The SDK's `["tags"]` array
|
|
246
|
+
returns `INVALID_FIELD_MASK`.
|
|
247
|
+
- **Guessing the item type.** Use the values in the Discover section. A store
|
|
248
|
+
product is `STORES_PRODUCT`, not `StoresProduct`, `product`, or `Product`.
|
|
249
|
+
|
|
250
|
+
- **Issuing a verification Get after a successful Set.** The Set response
|
|
251
|
+
already carries `resolvedTags`. A second read is redundant.
|
|
252
|
+
- **Using Products v1 or filtering by `name` on Query Products.** Both return
|
|
253
|
+
400. Use Search Products v3: `POST /stores/v3/products/search` with
|
|
254
|
+
`{"search":{"search":{"expression":"..."}}}`.
|
|
255
|
+
- **Calling List Item SEO Tags expecting product names.** List returns IDs
|
|
256
|
+
and tags, not names. Use Search Products to find the ID by name first.
|
|
257
|
+
- **Retrying a 400 with a different request shape without checking why.** A 400
|
|
258
|
+
means the shape was wrong. Compare against the shapes in this recipe, fix the
|
|
259
|
+
mismatch, send once. Three retries with guessed shapes is three wasted calls.
|
|
260
|
+
|
|
261
|
+
Tags can currently be written only for the site's primary language: leave
|
|
262
|
+
`language` unset on every write. There is no revision checking — the last write
|
|
263
|
+
wins and dashboard edits write to the same data, so read immediately before
|
|
264
|
+
writing.
|
|
265
|
+
|
|
266
|
+
**Static pages** keep a draft and a published revision: a write updates the
|
|
267
|
+
draft unless `publish` is `true`, and `publish: true` updates only the
|
|
268
|
+
published revision. Say clearly which revision the change reached. Item types
|
|
269
|
+
that are always live, such as store products, need no publish step.
|
|
270
|
+
|
|
271
|
+
## Bulk writes
|
|
272
|
+
|
|
273
|
+
To set tags on many items, call **Bulk Set Item SEO Tags** once, not Set in a
|
|
274
|
+
loop. Expected per-item failures (invalid tags, item not found) fail only that
|
|
275
|
+
entry and are reported on that entry's `itemMetadata.error`; map results back
|
|
276
|
+
to the request with `originalIndex` and retry only the failed entries. Report
|
|
277
|
+
partial failures truthfully — never present a partially failed bulk write as a
|
|
278
|
+
success.
|
|
279
|
+
|
|
280
|
+
## Present results
|
|
281
|
+
|
|
282
|
+
- When reporting what a page will render with, use the `resolvedTags` already
|
|
283
|
+
returned by the last Get or Set — not a fresh call — and name the source of
|
|
284
|
+
each tag (site, default pattern, user pattern, host page, or the item
|
|
285
|
+
itself). Naming the source of each reported tag is required, not optional.
|
|
286
|
+
`hasOverride: false` does not mean built-in defaults — the item may inherit
|
|
287
|
+
from a customized pattern or the site.
|
|
288
|
+
- `resolvedTags` excludes tags added by site code or apps at render time, so
|
|
289
|
+
present it as what Wix manages, not a literal copy of the rendered page head.
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
## Recovery rules
|
|
293
|
+
|
|
294
|
+
- **Permission denied:** stop after the first `403` or `PERMISSION_DENIED`. Do
|
|
295
|
+
not retry with another item, level, request shape, or site. Explain that the
|
|
296
|
+
current Wix identity lacks **Manage SEO Settings** and must be reconnected or
|
|
297
|
+
authorized. Never imply that a successful read means the write ran.
|
|
298
|
+
- **`UNSUPPORTED_ITEM_TYPE`:** the error message lists the item types the API
|
|
299
|
+
supports; use it to redirect the request rather than retrying blindly.
|
|
300
|
+
- **`ITEM_NOT_FOUND`:** re-discover the item ID; do not guess a new one.
|
|
301
|
+
- **Invalid tags:** tags are validated before anything is saved, so nothing
|
|
302
|
+
changed; fix the tag and resend the same complete set.
|
|
303
|
+
- **`400` on a request you built from this recipe:** compare the request you
|
|
304
|
+
sent against the shapes in this recipe's "REST request and response shapes"
|
|
305
|
+
section. Fix the mismatch — a wrong nesting level, a missing wrapper key, or
|
|
306
|
+
`fieldMask` sent as an array instead of a string — and send again. Never
|
|
307
|
+
resend the same shape, and never walk through variations hoping one is
|
|
308
|
+
accepted.
|
|
309
|
+
- **Setting tags for `EVENTS_PAGE` items:** not supported yet, although reading
|
|
310
|
+
them is; say so instead of retrying.
|
|
311
|
+
|
|
312
|
+
This recipe is self-contained for the common flows: request shapes, field
|
|
313
|
+
masks, tag structure, `resolvedTags` format, discovery, and error handling are
|
|
314
|
+
all above. Do not read the method's reference article and do not search the
|
|
315
|
+
API schemas — build every request from this recipe alone.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Manage URL Redirects on a Wix Site"
|
|
3
|
+
description: "Retrieve, create, and delete URL redirects on a Wix site using the public SEO Redirects API. Covers exact and group redirects, language-scoped redirects for multilingual sites, batches of up to 500, and the change flow for a redirect that already exists. This API has no query, search, or update method: List Redirects is the only read-many. Redirects do not chain, so creating one that points at a path another redirect starts from permanently deletes that other redirect; list and check before every write."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Manage URL Redirects on a Wix Site
|
|
7
|
+
|
|
8
|
+
Use the public **SEO Redirects API** to manage where a Wix site sends visitors who
|
|
9
|
+
request an old or moved URL. The API selects the site from the caller's
|
|
10
|
+
authorization context.
|
|
11
|
+
|
|
12
|
+
If the run already identifies a site - one connected site, or a site ID supplied
|
|
13
|
+
by the environment - use it and continue. Ask which site only when several are
|
|
14
|
+
available and nothing in the request or the context picks one. Do not stop to ask
|
|
15
|
+
for a site ID the API never takes.
|
|
16
|
+
|
|
17
|
+
A redirect returns a 301 permanent redirect, takes effect on the live site
|
|
18
|
+
immediately with no site publish, and **takes precedence over a real page at the
|
|
19
|
+
same path**. Creating a redirect from a path that still serves a page makes that
|
|
20
|
+
page unreachable until the redirect is deleted.
|
|
21
|
+
|
|
22
|
+
## Redirects do not chain, they replace
|
|
23
|
+
|
|
24
|
+
This is the single most important thing to know, and it is not what most agents
|
|
25
|
+
assume. If an existing redirect **starts at** the path your new redirect points
|
|
26
|
+
to, the two do not form a chain. The existing one is **deleted**, and your create
|
|
27
|
+
proceeds.
|
|
28
|
+
|
|
29
|
+
So with `/blog` -> `/news` already on the site, creating `/old-blog` -> `/blog`
|
|
30
|
+
does **not** produce `/old-blog` -> `/blog` -> `/news`. It produces
|
|
31
|
+
`/old-blog` -> `/blog`, and `/blog` -> `/news` is gone for good. Never describe
|
|
32
|
+
the result as a chain, a hop, or a redirect sequence.
|
|
33
|
+
|
|
34
|
+
## Run this check before every create
|
|
35
|
+
|
|
36
|
+
Do not skip it because the user's request was explicit. The user asking for a
|
|
37
|
+
redirect is not the same as the user agreeing to lose a different one.
|
|
38
|
+
|
|
39
|
+
1. Call **List Redirects**.
|
|
40
|
+
2. Compare the redirect you are about to create against every existing one:
|
|
41
|
+
- an existing redirect whose `from` equals your new `to` **will be deleted**
|
|
42
|
+
(loop resolution, described above);
|
|
43
|
+
- an existing redirect whose `from` equals your new `from` means your create
|
|
44
|
+
**fails** with `FROM_URL_EXISTS` and writes nothing.
|
|
45
|
+
- Paths differing only by a trailing slash count as equal for both checks.
|
|
46
|
+
3. **If either matched, end your turn without writing.** Name the exact redirect
|
|
47
|
+
at stake and what happens to it, then stop and wait for the user's answer.
|
|
48
|
+
|
|
49
|
+
This is a rule about turn structure, not about intent: **never put a create in
|
|
50
|
+
the same response as the List Redirects that found the conflict.** Reporting
|
|
51
|
+
the conflict and creating the redirect anyway is the failure this rule exists
|
|
52
|
+
to prevent - the user cannot answer a question you already acted on. If your
|
|
53
|
+
next action after listing is a write, you have skipped this step.
|
|
54
|
+
4. If neither matched, no deletion is possible: create it in the same turn and
|
|
55
|
+
report the result. Do not ask permission you do not need.
|
|
56
|
+
|
|
57
|
+
`options.forceReplace` is the only way to overwrite a taken `from` path, and it
|
|
58
|
+
**deletes** the redirect holding it. Never set it on your own initiative. Offer it
|
|
59
|
+
only after reporting the conflict, and only if the user asks to replace.
|
|
60
|
+
|
|
61
|
+
## The six methods and their request shapes
|
|
62
|
+
|
|
63
|
+
Every path is under `https://www.wixapis.com/seo-redirects-service/v1`. Use these
|
|
64
|
+
directly - do not go looking for the shapes in the docs first.
|
|
65
|
+
|
|
66
|
+
| Method | Call |
|
|
67
|
+
|---|---|
|
|
68
|
+
| List Redirects | `GET /redirects` - no body, no parameters |
|
|
69
|
+
| Get Redirect | `GET /redirects/{redirectId}` |
|
|
70
|
+
| Create Redirect | `POST /create-redirect` - body below |
|
|
71
|
+
| Delete Redirect | `DELETE /redirects/{redirectId}` - empty response |
|
|
72
|
+
| Bulk Create Redirects | `POST /bulk/redirects/create` |
|
|
73
|
+
| Bulk Delete Redirects | `POST /bulk/redirects/delete` |
|
|
74
|
+
|
|
75
|
+
Create Redirect takes the redirect nested under `redirect`. `options` and
|
|
76
|
+
`language` are optional, `id` only when preserving an existing redirect's
|
|
77
|
+
identity:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"redirect": {
|
|
82
|
+
"from": "/old-blog",
|
|
83
|
+
"to": "/blog",
|
|
84
|
+
"options": { "groupRedirect": true },
|
|
85
|
+
"language": "fr"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The two bulk methods take flat lists. Bulk Create's `returnFullEntity` returns
|
|
91
|
+
each created redirect in `results[].item`:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{ "redirects": [ { "from": "/old-pricing", "to": "/pricing" } ], "returnFullEntity": true }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{ "redirectIds": ["5b07d9de-6a9f-494d-8066-a7b66c970270"] }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Worked example: the request that deletes something
|
|
102
|
+
|
|
103
|
+
The user asks to send `/old-blog` and everything under it to `/blog`.
|
|
104
|
+
|
|
105
|
+
**1. List first.**
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
GET https://www.wixapis.com/seo-redirects-service/v1/redirects
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"redirects": [
|
|
114
|
+
{
|
|
115
|
+
"id": "583fe7d7-2944-436f-870a-09eb6d2e52fa",
|
|
116
|
+
"from": "/blog",
|
|
117
|
+
"to": "/news",
|
|
118
|
+
"createdDate": "2026-08-20T09:12:44.000Z"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**2. Compare.** The new `to` is `/blog`, and an existing redirect's `from` is
|
|
125
|
+
`/blog`. That is the deletion case. **End the turn here** - report that creating
|
|
126
|
+
this redirect deletes `/blog` -> `/news` permanently, and that the two will not
|
|
127
|
+
chain, then wait.
|
|
128
|
+
|
|
129
|
+
**3. Only after the user agrees**, create it:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
POST https://www.wixapis.com/seo-redirects-service/v1/create-redirect
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{ "redirect": { "from": "/old-blog", "to": "/blog", "options": { "groupRedirect": true } } }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"redirect": {
|
|
142
|
+
"id": "9c4a1f2e-77b0-4b13-a2c8-6e0d3f5b8a91",
|
|
143
|
+
"from": "/old-blog",
|
|
144
|
+
"to": "/blog",
|
|
145
|
+
"options": { "groupRedirect": true }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`/blog` -> `/news` is now gone. Say so - do not report it as still in place, and
|
|
151
|
+
do not describe the result as `/old-blog` -> `/blog` -> `/news`.
|
|
152
|
+
|
|
153
|
+
## When a write fails
|
|
154
|
+
|
|
155
|
+
Report the failure and stop. Do **not** retry with a different request shape, a
|
|
156
|
+
different path, `forceReplace` added, or a bulk call in place of a single one. A
|
|
157
|
+
failed create wrote nothing, so there is nothing to clean up, and a mutated retry
|
|
158
|
+
is a second attempt at a decision the user has not agreed to.
|
|
159
|
+
|
|
160
|
+
On the first `403` or `PERMISSION_DENIED`, stop and report the missing
|
|
161
|
+
authorization for managing SEO settings. Do not try another site, path, or method.
|
|
162
|
+
|
|
163
|
+
## Choose exact or group
|
|
164
|
+
|
|
165
|
+
| User intent | Setting |
|
|
166
|
+
|---|---|
|
|
167
|
+
| "Redirect this one URL" | omit `options`, or `options.groupRedirect: false` |
|
|
168
|
+
| "Redirect this section", "everything under /blog" | `options.groupRedirect: true` |
|
|
169
|
+
|
|
170
|
+
A group redirect carries the rest of the URL over to the target: from
|
|
171
|
+
`/forum/questions/` to `/forum/faqs/` sends `/forum/questions/my-post` to
|
|
172
|
+
`/forum/faqs/my-post`. An exact and a group redirect that share a `from` path are
|
|
173
|
+
two different redirects.
|
|
174
|
+
|
|
175
|
+
For a multilingual site, set `language` to apply the redirect to one language
|
|
176
|
+
only; omit it to apply to every language. A language-scoped path is stored
|
|
177
|
+
**without** the language prefix, so `/fr/about` with `language: "fr"` comes back
|
|
178
|
+
as `/about`. Deleting a language-scoped redirect stops it working but it can
|
|
179
|
+
still appear in List Redirects.
|
|
180
|
+
|
|
181
|
+
`from` cannot be the site root, and two paths differing only by a trailing slash
|
|
182
|
+
count as the same path.
|
|
183
|
+
|
|
184
|
+
## Read: List Redirects is the only way to read many
|
|
185
|
+
|
|
186
|
+
**There is no query endpoint and no search endpoint.** Most Wix APIs have one, so
|
|
187
|
+
this is the second place an agent goes wrong. Do not call, construct, or go
|
|
188
|
+
looking for `POST /v1/redirects/query`, a `/search` path, or any filtered variant:
|
|
189
|
+
they do not exist, and trying one wastes a turn and returns nothing useful.
|
|
190
|
+
|
|
191
|
+
- **List Redirects** returns every redirect on the site in one response. It takes
|
|
192
|
+
no filter, sort, cursor, or paging parameter at all. Filter, sort, and slice the
|
|
193
|
+
returned array yourself. It also returns redirects Wix created for the site
|
|
194
|
+
owner, such as when a page's URL slug was renamed in the editor.
|
|
195
|
+
- **Get Redirect** retrieves one by ID.
|
|
196
|
+
|
|
197
|
+
## Change an existing redirect
|
|
198
|
+
|
|
199
|
+
There is **no update method**, and as above no query method. To change a redirect:
|
|
200
|
+
|
|
201
|
+
1. Call **Get Redirect** and keep the whole response.
|
|
202
|
+
2. Call **Delete Redirect** with the same ID.
|
|
203
|
+
3. Call **Create Redirect** with every field you retrieved, with your change applied.
|
|
204
|
+
|
|
205
|
+
Carry the whole redirect across, not only what changed. Create Redirect fills an
|
|
206
|
+
omitted field with its default rather than the previous value, so dropping
|
|
207
|
+
`options` turns a group redirect into an exact one and dropping `language` turns a
|
|
208
|
+
language-scoped redirect into a global one. Keep `id` to preserve the redirect's
|
|
209
|
+
identity, or omit it for a new one.
|
|
210
|
+
|
|
211
|
+
Confirm with the user before step 2: between the delete and the create the
|
|
212
|
+
redirect is not in effect, and if the create fails the old redirect is gone.
|
|
213
|
+
|
|
214
|
+
## Batches
|
|
215
|
+
|
|
216
|
+
**Bulk Create Redirects** and **Bulk Delete Redirects** take 1 to 500 items and
|
|
217
|
+
report each one separately **inside a successful response**:
|
|
218
|
+
|
|
219
|
+
- Read every outcome from `results[].itemMetadata`, matched to your request by
|
|
220
|
+
`originalIndex`. A failed item carries `error.code`, such as `FROM_URL_EXISTS`
|
|
221
|
+
or `REDIRECT_NOT_FOUND`.
|
|
222
|
+
- `bulkActionMetadata.undetailedFailures` counts items whose outcome is
|
|
223
|
+
**unknown**: they carry no error and may or may not have been written. Call
|
|
224
|
+
List Redirects to find out. Never report them as successes.
|
|
225
|
+
- A malformed request, such as an empty list, is rejected as a whole with a 400
|
|
226
|
+
and returns no per-item results.
|
|
227
|
+
|
|
228
|
+
Two more sharp edges to report truthfully:
|
|
229
|
+
|
|
230
|
+
- A successful **Bulk Delete** item means the deletion was found and sent, not
|
|
231
|
+
confirmed. Call List Redirects if the user needs confirmation.
|
|
232
|
+
- Bulk Create is **not atomic**. If an item's conflicting or loop-causing
|
|
233
|
+
redirect is deleted and the write then fails, the deleted redirect is gone and
|
|
234
|
+
nothing replaces it. Repeating the same request recreates it.
|
|
235
|
+
|
|
236
|
+
Creating a redirect identical to one already on the site changes nothing and
|
|
237
|
+
succeeds: Create Redirect echoes the request back without an `id` or
|
|
238
|
+
`createdDate`, and Bulk Create reports success with no `id`. Do not present that
|
|
239
|
+
as a newly created redirect.
|
|
240
|
+
|
|
241
|
+
## Reading errors
|
|
242
|
+
|
|
243
|
+
- On a 400, read `details.validationError.fieldViolations` and report the named
|
|
244
|
+
field rather than guessing.
|
|
245
|
+
- **Known divergence:** the reference documents `REDIRECT_NOT_FOUND` for an
|
|
246
|
+
unknown redirect ID on Get Redirect and Delete Redirect. The API currently
|
|
247
|
+
returns a generic 404 without that code. Treat any 404 on those two methods as
|
|
248
|
+
"no redirect with that ID", and do not tell the user the ID was valid.
|
|
249
|
+
- No webhook or event is emitted when a redirect is created or deleted. Never
|
|
250
|
+
treat the absence of an event as the absence of a change.
|