@lacneu/wix-openclaw
v0.2.2
Published
Wix REST API plugin for OpenClaw — manage blog, CMS data, forms, bookings, contacts, events, FAQ on a single Wix site with site_id whitelist and approval gating on destructive ops
Maintainers
Readme
wix-openclaw-plugin
Wix REST API plugin for OpenClaw Manage blog, CMS data, forms, bookings, contacts, events, FAQ on a single Wix site — with site_id whitelist and approval gating on every destructive operation.
Overview
wix-openclaw is an OpenClaw plugin that lets your agent operate a Wix site
through the official Wix REST API. It registers ~50 tools split across a dozen
Wix products (Blog, CMS Data, Forms, Bookings, Contacts, Events, Reviews, FAQ,
Multilingual, Sites, Site Media), plus a wix_design_brief helper that turns
loose textual briefs into structured markdown artefacts.
Every tool funnels through a single authenticated HTTP client that:
- Injects the correct headers (
Authorization,wix-account-id,wix-site-id) - Refuses any call to a site UUID not in
allowedSiteIds— before the network round trip - Retries on 429/5xx with exponential backoff
- Maps non-retryable errors to clean tool failures (
WixApiError)
Destructive operations (publish, delete, cancel, moderate) trigger a
before_tool_call approval prompt that the operator must accept in the
Control UI / Telegram before the call goes through.
Why a whitelist?
Wix accounts can host many sites. The wix-site-id header is the only thing
between an agent and "we just published a draft on the wrong site".
allowedSiteIds is enforced in the client itself, before any HTTP request
is built. If the LLM hallucinates a site UUID, the call fails locally — no
traffic is generated and no Wix logs are polluted. This is the same reason
google_workspace enforces a domain allowlist: prompt injection should never
reach the wrong tenant.
Installation
Requirements
- OpenClaw
>= v2026.4.0 - A Wix REST API key with the minimum required permissions (see Wix API Key permissions below)
- The Wix account UUID and at least one site UUID
Install via OpenClaw CLI
openclaw plugins install @lacneu/wix-openclaw
openclaw plugins inspect @lacneu/wix-openclawConfiguration
Add the entry to your openclaw.json:
{
"plugins": {
"allow": ["wix-openclaw", "openclaw-knowledge", "telegram"],
"entries": {
"wix-openclaw": {
"enabled": true,
"config": {
"apiKey": "${WIX_API_KEY}",
"accountId": "${WIX_ACCOUNT_ID}",
"allowedSiteIds": ["${WIX_SITE_ID_ATARAXIS}"],
"defaultSiteId": "${WIX_SITE_ID_ATARAXIS}",
"logLevel": "info"
}
}
}
}
}Add the secrets to your .env:
WIX_API_KEY=<your-wix-api-key>
WIX_ACCOUNT_ID=<your-wix-account-uuid>
WIX_SITE_ID_ATARAXIS=<your-target-site-uuid>Restart the gateway:
openclaw gateway restartConfiguration reference
| Field | Type | Default | Description |
|---|---|---|---|
| enabled | boolean | true | Master switch — disables all wix_* tools when false |
| apiKey | string | — | Wix REST API key. Sent raw in Authorization (no Bearer prefix). Supports ${ENV_VAR} |
| accountId | string | — | Wix account UUID. Sent as wix-account-id. Supports ${ENV_VAR} |
| allowedSiteIds | string[] | [] | Whitelist of Wix site UUIDs. Calls to other sites are rejected before the network round trip |
| defaultSiteId | string | first of allowedSiteIds | Site UUID injected when a tool does not explicitly specify one |
| approvalRequired | string[] | (every destructive tool) | Tool names that trigger a before_tool_call approval prompt |
| logLevel | enum | info | debug / info / warn / error. debug logs request bodies (mind PII) |
Authentication header convention
Wix REST APIs use a slightly non-standard auth pattern: the API key goes raw
in the Authorization header — without the Bearer prefix.
Authorization: <wix-api-key>
wix-account-id: <wix-account-uuid>
wix-site-id: <wix-site-uuid>This plugin handles that for you; you only need to provide the three values.
Wix API Key permissions
A Wix REST API key has account-wide reach by design — it can target any
of the sites under the Wix account it belongs to. Wix does not let you
scope a key to a single site at creation time. The plugin compensates with
its allowedSiteIds whitelist (cf. Why a whitelist?),
but the API key permission set itself remains your most important defense
against accidental — or malicious — over-reach. Treat it as a
least-privilege configuration exercise.
Where to configure them Wix Dashboard → Settings → Headless Settings → API Keys → click your key → Edit API Key → Permissions.
Minimum required (for the V1 toolset shipped here)
Tick only the following permissions on your API key. Everything else should stay unticked.
Basic permissions
- [x] Get Sites List — required to discover and validate site UUIDs
Site permissions
- [x] Wix Blog — drafts, posts, categories, tags
- [x] Manage Site Media — upload images for blog cover & rich content
- [x] Business Info — name, address, hours, timezone
- [x] Wix Data — custom CMS collections (read & write)
- [x] Wix Forms — read submitted forms
- [x] Manage Form Submissions — read leads / contact requests
- [x] Wix Contacts & Members — CRM contacts + members
- [x] Wix Bookings — services, sessions, bookings
- [x] Wix Events — events, RSVPs, guests
- [x] Wix Reviews — list & moderate reviews
- [x] Manage FAQ — FAQ categories and questions
- [x] Multilingual Translation Schema Read — read site translations
- [x] List Marketing Tags — read-only marketing/analytics tags
Optional, depending on the business model
Tick these only if the corresponding Wix product is actually in use on the target site:
- [ ] Pricing Plans — if you sell subscription packages
- [ ] Wix Stores + Wix eCommerce — if you sell physical/digital
products. Without these, the
wix_*tools that touch e-commerce resources (none in V1) cannot run.
Permissions to never tick (defense in depth)
The plugin does not need any of the following, and granting them needlessly amplifies the blast radius if the key ever leaks. Audit your key periodically and keep these unticked:
| Permission | Why it's dangerous |
|---|---|
| Manage Sites | Allows site deletion |
| Manage Domains | Allows transferring your domain away |
| Publish Metasite | Allows un-publishing the entire site |
| Manage Embedded Scripts | Allows arbitrary JavaScript injection on every page |
| OAuth Apps | Allows creating persistent OAuth apps that survive key revocation |
| Manage Functions / Manage Your App | Arbitrary server-side code |
| Connect Wix Payments Account / Wix Payments / Wix Cashier | Reroutes customer payments |
| Manage Email Marketing / Manage Email Subscriptions | Mass-email spam & subscription tampering |
| Server Sign On for Members | Lets the holder impersonate any member |
| Manage Roles / Manage Site Members | Account take-over |
| Wix CLI - Git Integration | Modify deployed code |
| Multilingual Translation Schema Write | Tamper with on-site copy across languages |
| Manage Notifications | Push spam to members |
| Invoke AI Models | Bills your account for arbitrary LLM usage |
| Manage Wixel Projects / Manage Site Branches | Layout/structure mutation |
| Any All account permissions master toggle | Unties every account-wide capability above |
Mapping: tool group → required permission
Use this table to figure out which permission a given group of tools relies on. If you want a slimmer key (e.g. blog-only first), drop entire columns and remove the matching tools from your agent's allow-list.
| Tool group | Wix permission | Required if you use… |
|---|---|---|
| wix_sites_list, wix_site_url_get | Get Sites List | Always (used internally — wix_site_url_get reads viewUrl from the Site object, no separate "Read Site URLs" permission needed) |
| wix_business_info_get | Business Info | wix_business_info_get |
| wix_blog_* | Wix Blog | Any blog tool |
| wix_media_* | Manage Site Media | Any media tool, and rich-content blog drafts |
| wix_data_* | Wix Data | Any CMS collection tool |
| wix_forms_* | Wix Forms + Manage Form Submissions | Lead reading |
| wix_contacts_* | Wix Contacts & Members | CRM tooling |
| wix_bookings_* | Wix Bookings | Booking tooling |
| wix_events_* | Wix Events | Event tooling |
| wix_reviews_* | Wix Reviews | Reviews moderation |
| wix_faq_* | Manage FAQ | FAQ tooling |
| wix_multilingual_* | Multilingual Translation Schema Read | Translation reads |
| wix_design_brief | (none — no API call) | Always |
Wix app installation requirements
Some Wix REST endpoints only exist when the corresponding Wix app is installed on the target site. The plugin ships them all generically — they will simply return a clean error when the app is missing, and start working as soon as the app is installed (no plugin upgrade needed).
| Tool group | Wix app required | Error returned when missing |
|---|---|---|
| wix_blog_* | Wix Blog | 404 |
| wix_data_* | Velo / Wix Code (enable in Editor) | 400 WDE0110: Wix Code not enabled |
| wix_forms_* | Wix Forms | 404 |
| wix_bookings_* | Wix Bookings | 404 |
| wix_events_* | Wix Events | 428 WIX_EVENTS_APP_NOT_INSTALLED |
| wix_reviews_* | Wix Reviews | 428 APP_NOT_INSTALLED |
| wix_faq_* | Wix FAQ | 400 App is not installed |
| wix_multilingual_* | Wix Multilingual | 403 |
| wix_contacts_*, wix_media_*, wix_sites_list, wix_site_url_get, wix_business_info_get | (always available) | — |
To install a Wix app on your site: Wix Dashboard → App Market → search for the app → Add to Site. It is free in most cases and takes effect immediately for API calls.
Rotating the key
Wix keys can be revoked at any time from the same API Keys page. If a key leaks, or if you need to ship a temporary smoke-test key with narrower scope:
- Generate a new key with the desired scope.
- Update
WIX_API_KEYin your environment. - Restart the OpenClaw gateway.
- Revoke the old key.
The plugin reads the key on each request through the resolved config — no in-memory caching beyond the gateway lifecycle, so a restart fully flushes the previous credential.
Wix query envelope
Every POST /query tool (wix_contacts_query, wix_bookings_*_list,
wix_forms_list_submissions, wix_events_*, wix_reviews_list,
wix_multilingual_list_languages, wix_sites_list,
wix_data_query_items) accepts the standard Wix Query Language
envelope as plain optional parameters:
{
// Optional — MongoDB-style filter, e.g.
// { "status": { "$in": ["ACTIVE", "PENDING"] } }
// { "$and": [{ "createdDate": { "$gt": "2026-01-01T00:00:00Z" } },
// { "info.emails.email": { "$contains": "acme" } }] }
"filter": { /* ... */ },
// Optional — sort entries applied in order
"sort": [{ "fieldName": "createdDate", "order": "DESC" }],
// Optional — offset paging (most endpoints)
"paging": { "limit": 50, "offset": 0 },
// Optional — cursor paging (Site Media, some Bookings, ...)
"cursorPaging": { "limit": 50, "cursor": "<opaque>" },
// Optional — projection
"fields": ["id", "info.name"]
}Supported operators (per the Wix Query Language docs):
$eq, $ne, $gt, $gte, $lt, $lte, $in, $nin,
$exists, $startsWith, $contains, $hasSome, $hasAll,
$matches, $and, $or, $not.
Live verification scope The plugin's smoke test currently exercises
$eq,$in,$exists, field equality, sort entries, offset paging, and free-textsearchagainst the Ataraxis site. Other operators ($ne,$gt,$gte,$lt,$lte,$nin,$startsWith,$contains,$hasSome,$hasAll,$matches,$or,$not) andcursorPaging/fieldsprojection are exposed structurally and routed to the body by the mapper, but per-operator live coverage on every endpoint is not in v0.2.0's scope. Each Wix endpoint individually decides which fields are filterable and which operators are accepted — Wix returns a clean400 UNSUPPORTED_QUERY_FILTERwhen it disagrees, surfaced as a tool failure to the model.
Tools that have ergonomic shortcuts (e.g. wix_blog_list_drafts.status,
wix_forms_list_submissions.namespace) always merge the shortcut
into the filter via $and, so the agent can never silently override the
shortcut by passing a competing filter.
Flat GET endpoints (wix_blog_list_drafts, wix_blog_list_published,
wix_blog_list_categories, wix_blog_list_tags,
wix_data_list_collections, wix_faq_list_*, wix_media_list,
wix_multilingual_list_schemas) accept named query string params plus
paging.{limit,offset,cursor} — they do NOT support the operator
passthrough above. Each tool's parameters are documented in its
description.
Tools
The Validated column reflects v0.2.0 verification on the Ataraxis
Coaching site: live means hit by the smoke-test against a real Wix
site; app-not-installed and velo-not-enabled mean the tool's
endpoint exists but its backing Wix product is not active on the
smoke-test site (the request still reaches Wix and the path/headers
are correct); docs means write/destructive endpoints that we do not
exercise live.
Blog
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_blog_list_drafts | List drafts; supports status (UNPUBLISHED \| PUBLISHED \| SCHEDULED \| IN_REVIEW \| ALL), sort, fieldsets, paging | — | live |
| wix_blog_get_draft | Fetch a draft by id; supports fieldsets | — | live |
| wix_blog_create_draft | Create a new draft | — | docs |
| wix_blog_update_draft | Patch a draft | — | docs |
| wix_blog_publish_draft | Publish a draft | required | docs |
| wix_blog_list_published | List published posts; supports sort, featured, language, tagIds, categoryIds, fieldsets, paging | — | live |
| wix_blog_get_post | Fetch a published post; supports fieldsets | — | live |
| wix_blog_unpublish | Move post to trash (reversible) | required | docs |
| wix_blog_delete_draft | Permanently delete a draft | required | docs |
| wix_blog_list_categories | List blog categories; supports language, paging | — | live |
| wix_blog_list_tags | List blog tags; supports language, paging | — | live |
Media
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_media_upload | Import image into Site Media by URL | — | docs |
| wix_media_list | List files; supports parentFolderId, mediaTypes (IMAGE/VIDEO/AUDIO/DOCUMENT/ARCHIVE), cursor paging via paging.cursor | — | live |
The wix_media_upload tool returns the Wix file metadata, including the
id. Pass that id back into wix_blog_create_draft as
coverMedia.imageId or embed it into a Ricos rich-content node — see
the tool description for the contract the LLM should follow.
Site
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_sites_list | List sites under the account (account-scoped); accepts the standard Wix query envelope | — | live |
| wix_site_url_get | Get published URL of a site | — | live |
| wix_business_info_get | Read business info (name, address, hours) | — | live |
CMS Data
Requires Velo / Wix Code to be enabled on the target site (Wix Editor →
Velo → Enable Code). Without it, calls return HTTP 400 WDE0110: Wix
Code not enabled.
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_data_list_collections | List CMS collections + schemas | — | velo-not-enabled |
| wix_data_query_items | Full Wix query envelope on a collection | — | velo-not-enabled |
| wix_data_get_item | Fetch one item by id | — | velo-not-enabled |
| wix_data_insert_item | Insert a new item | — | docs |
| wix_data_update_item | Update an item | — | docs |
| wix_data_remove_item | Delete an item | required | docs |
Forms
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_forms_list_submissions | Full Wix query envelope; namespace defaults to wix.form_app.form and is always $and-merged with passthrough filter | — | live |
| wix_forms_get_submission | Fetch a single submission | — | docs |
Contacts
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_contacts_query | Full Wix query envelope + free-text search | — | live |
| wix_contacts_get | Get one contact | — | docs |
| wix_contacts_create | Create a contact | — | docs |
| wix_contacts_update | Update a contact | — | docs |
| wix_contacts_label_add | Add labels to a contact | — | docs |
| wix_contacts_delete | Delete a contact | required | docs |
Bookings
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_bookings_services_list | List bookable services; standard query envelope | — | live |
| wix_bookings_query_bookings | Query bookings with operators (e.g. status: { $in: [...] }); standard query envelope | — | live |
| wix_bookings_get_booking | Fetch one booking | — | docs |
| wix_bookings_create | Create a booking | — | docs |
| wix_bookings_reschedule | Move to a different slot | required | docs |
| wix_bookings_cancel | Cancel a booking | required | docs |
Events
Requires the Wix Events app (returns 428 WIX_EVENTS_APP_NOT_INSTALLED
otherwise).
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_events_list | List events; standard query envelope | — | app-not-installed |
| wix_events_get | Fetch one event | — | app-not-installed |
| wix_events_list_guests | List guests / RSVPs; eventId is $and-merged with passthrough filter | — | app-not-installed |
Reviews
Requires the Wix Reviews app (returns 428 APP_NOT_INSTALLED otherwise).
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_reviews_list | List reviews; standard query envelope | — | app-not-installed |
| wix_reviews_get | Fetch one review | — | docs |
| wix_reviews_moderate | Approve / reject a pending review | required | docs |
FAQ
Requires the Wix FAQ app (returns 400 App is not installed otherwise).
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_faq_list_categories | List FAQ categories | — | app-not-installed |
| wix_faq_list_questions | List questions (filterable by category) | — | app-not-installed |
| wix_faq_create_question | Create a question | — | docs |
| wix_faq_update_question | Update a question | — | docs |
| wix_faq_delete_question | Delete a question | required | docs |
Multilingual (read-only in V1)
Returns 403 on the Ataraxis smoke-test site — likely missing the
Multilingual Translation Schema Read permission on the API key
rather than an app-not-installed condition.
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_multilingual_list_languages | List configured languages; standard query envelope | — | 403-permission |
| wix_multilingual_list_schemas | List translation schemas | — | 403-permission |
Design
| Tool | What it does | Approval | Validated |
|---|---|---|---|
| wix_design_brief | Turn a loose textual brief into a structured markdown artefact (palette, typography, sections, image-gen prompts). Does NOT call the Wix API. | — | n/a |
Approval flow
When the LLM calls a destructive tool, the plugin's before_tool_call hook
fires and returns a requireApproval directive:
{
"requireApproval": {
"title": "Wix: confirm wix_blog_publish_draft",
"description": "Tool `wix_blog_publish_draft` is about to run with the following parameters:\n\n```json\n{ \"draftPostId\": \"abc-123\" }\n```\n...",
"severity": "critical",
"timeoutMs": 600000,
"timeoutBehavior": "deny"
}
}OpenClaw surfaces this to whichever channel/UI the operator is using (Telegram inline buttons, Control UI badge, etc.). If no decision is made within 10 minutes, the call is denied automatically.
You can override the gated set via approvalRequired in the plugin config —
use an empty array to disable gating entirely (not recommended in production).
Recommended companion skill
OpenClaw plugins ship code (tools, hooks); they do not ship skills. Skills live in agent workspaces and carry the business context that makes the LLM use the tools correctly (audience, tone, recipes, guardrails).
To get an agent up and running with this plugin, copy
examples/recommended-skill.md into the
target workspace at ~/.openclaw/workspace-<agent>/skills/wix/SKILL.md,
then replace the <PLACEHOLDER> values with your own (organisation name,
domain, contributors, tone). Duplicate the file for each agent that
should benefit from the skill.
The template covers:
- Conversational triggers (encoded in the
descriptionfrontmatter as per OpenClaw skill convention) - 5 usage recipes (publish article, list real drafts, read leads, manage bookings, design request)
- Behavioural guardrails (approval recap before publish, app-not-installed handling, PII rules)
- Tool mapping (short reference; the README is the source of truth)
Development
Project layout
wix-openclaw-plugin/
├── src/
│ ├── index.ts # Entry point + tool collection
│ ├── config.ts # resolveEnv + default resolution
│ ├── wix-client.ts # Authenticated HTTP client + whitelist
│ ├── types.ts # Shared interfaces
│ ├── hooks/
│ │ └── approval.ts # before_tool_call approval gating
│ └── tools/
│ ├── _factory.ts # defineWixTool helper
│ ├── blog.ts
│ ├── media.ts
│ ├── site.ts
│ ├── data.ts
│ ├── forms.ts
│ ├── contacts.ts
│ ├── bookings.ts
│ ├── events.ts
│ ├── reviews.ts
│ ├── faq.ts
│ ├── multilingual.ts
│ └── design.ts
├── test/
│ ├── wix-client.test.ts
│ ├── manifest.test.ts
│ ├── hooks/approval.test.ts
│ └── tools/blog.test.ts
├── tsconfig.json
├── tsconfig.test.json
├── tsconfig.test-build.json
├── openclaw.plugin.json
└── package.jsonBuild and test
npm install
npm run typecheck # strict TS check (src + test)
npm test # compile tests + run node:test
npm run build # compile src/ → dist/
npm run clean # remove dist + dist-testRelease process
- Update
CHANGELOG.mdwith a new## [x.y.z] - YYYY-MM-DDsection - Commit
- Tag and push:
git tag v0.1.0 git push origin v0.1.0 - GitHub Actions will:
- Run typecheck, tests, build on Node.js 24
- Stamp the version into
package.jsonandopenclaw.plugin.json - Publish to npm via Trusted Publishing (OIDC) — no
NPM_TOKENsecret needed - Create a GitHub Release with the changelog excerpt
License
MIT — see LICENSE
