Subchapter 5.1
references/API_SPEC_SEARCH.mdMarkdown11 KBView on GitHub
curl (no MCP)A single curl endpoint that runs a JS query over the Wix REST API spec — the no-MCP equivalent of
the MCP SearchWixAPISpec / getResourceSchemaByUrl tools. It does two jobs:
lightIndexoperationIdhttpMethodmenuPathdocsUrlpublicUrl.mdSKILL.mdgetResourceSchemaByUrl(docsUrl) returns the exact request/response shape,
field types, enums, and error codes (the markdown pages bury these in huge inline schemas),
scoped to what you pass: a method URL → a schema holding just that method; a resource
URL (or getResourceSchema(resourceId)) → the whole resource, every method.Endpoint:
POST https://mcp.wix.com/api/code-mode/search— body{ "code": "<async function() {…}>" }. Thecodeis a JSasync function()that runs in a read-only sandbox and returns any JSON-serializable value. Response envelope:{ "result": <return value> }or{ "error": "<msg>" }— both with HTTP 200, so check the body forerror, never just the status.
Internal/undocumented and pre-GA — treat it as best-effort; the contract could change. For reading a single known page, the
.mdtwin inSKILL.mdis simpler — reach here when you specifically need the structured spec.
Send the function as the JSON string code. Multi-line functions are easiest to send by encoding
with a helper (keeps quotes/newlines valid):
read -r -d '' CODE <<'JS'
async function() {
return lightIndex.filter(r => r.menuPath.includes("bookings")).map(r => r.name);
}
JS
curl -sS -X POST 'https://mcp.wix.com/api/code-mode/search' \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg code "$CODE" '{code:$code}')" # or: python3 -c 'import json,sys;print(json.dumps({"code":sys.stdin.read()}))'A short function can go inline: --data-raw '{"code":"async function(){ return lightIndex.length; }"}'.
Shape the result inside the function and return only what you need — a whole resource schema is
~200 KB+, so never return await getResourceSchema(id) wholesale.
lightIndex — array of the REST API resources. Each entry:
| Field | Meaning |
|---|---|
name | Resource display name (e.g. "Products V3", "Bookings Writer V2") |
resourceId | Internal handle for getResourceSchema() |
docsUrl | Resource docs page |
menuPath | e.g. ["business-solutions","stores","catalog-v3","products-v3"] |
methods[] | { operationId, summary, httpMethod, path, docsUrl, publicUrl, publicBaseUrl, description } |
getResourceSchemaByUrl(docsUrl) (preferred when you have a URL) and
getResourceSchema(resourceId) — return the schema, scoped to the input: a method URL
scopes methods to that one method (read it as methods[0]); a resource URL or resourceId
returns every method. Shape:
{ title, description, fqdn, docsUrl,
methods: [ { summary, description, operationId, httpMethod, path, docsUrl,
publicUrl, publicBaseUrl, requestBody, responses, parameters,
permissions, queryMethodData, legacyExamples: [ { content: { title, request, response } } ] } ],
components: { schemas: { …every referenced type… } } }articles — array of the REST portal’s prose pages (introductions, recipes, flow pages) as
{ name, resourceId, docsUrl, menuPath, description };
getArticleContentByUrl(docsUrl) / getArticleContent(resourceId) — return an article’s
full markdown. This is the coverage lightIndex (methods only) doesn’t have; articles and resources
share the same menuPath hierarchy.
method.publicUrl — the complete https://www.wixapis.com/... URL. method.path
is a partial path (omits the gateway prefix like /stores) — never build a URL from it, and
never use method.servers[0] (internal hosts).responses alongside requestBody when inspecting a method — saves a re-run.$circular refs: schemas reference types as { "$circular": "TypeName" }, resolved via
schema.components.schemas["TypeName"]. Expand only the types you need (see the nested-refs
example); a full recursive expand can be huge.method.queryMethodData.queryFieldsCapabilitiesMap
lists which fields accept filters (and their operators) and sorting. A field absent from the map
is rejected by the API — filter it client-side after fetching a bounded page.m.docsUrl to the URL you passed in — the reader
normalizes URLs (.md, query params, casing), so equality against your raw input can miss. On a
method-URL fetch the scoped result is the method: read methods[0]. Need siblings? Fetch the
resource URL.getResourceSchemaByUrl resolves API method/resource URLs only — not /skills/… or article
pages (use getArticleContentByUrl for those). Follow the error, don’t retry it: the {error}
message names the fix — an article URL points you to the article reader; an unknown URL means
search lightIndex/articles by keyword instead of re-sending the same lookup. If discovery
still fails, report the limitation — don’t guess the contract.Each is an async function() — send it via the wrapper above.
Find APIs by broad keywords (when you don’t have a docs URL):
async function() {
const words = ["stores", "query", "products"];
return lightIndex.flatMap(resource =>
resource.methods
.filter(method => {
const haystack = [
resource.name, resource.docsUrl, resource.menuPath.join("/"),
method.summary, method.operationId, method.description, method.path, method.docsUrl
].join(" ").toLowerCase();
return words.every(word => haystack.includes(word));
})
.map(method => ({
title: method.summary, resource: resource.name,
httpMethod: method.httpMethod.toUpperCase(),
docsUrl: method.docsUrl, publicUrl: method.publicUrl
}))
);
}Inspect one method by its docs URL (request + response + query capabilities + curl examples).
A method URL returns a schema scoped to that method — methods[0] is it; don’t match on
m.docsUrl === methodUrl (the reader normalizes URLs):
async function() {
const methodUrl = "https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products";
const schema = await getResourceSchemaByUrl(methodUrl); // scoped: one method
const method = schema.methods[0];
return {
title: method.summary,
publicUrl: method.publicUrl,
httpMethod: method.httpMethod.toUpperCase(),
operationId: method.operationId,
permissions: method.permissions,
parameters: method.parameters,
requestBody: method.requestBody,
responses: method.responses,
queryFieldsCapabilities: method.queryMethodData?.queryFieldsCapabilitiesMap,
curlExamples: method.legacyExamples?.map(e => e.content)
};
}Inspect a whole resource by its docs URL (the method URL minus its last segment) — use this when you need sibling operations or the shared object schema (a requirement is often documented on a sibling method, e.g. required on single-create but omitted from bulk-create):
async function() {
const schema = await getResourceSchemaByUrl("https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3"); // resource URL → every method
return {
resource: schema.title,
description: schema.description,
methods: schema.methods.map(m => ({ title: m.summary, httpMethod: m.httpMethod.toUpperCase(), docsUrl: m.docsUrl, publicUrl: m.publicUrl, operationId: m.operationId }))
};
}Resolve one method from a partial docs URL (when you only have a path fragment):
async function() {
const partial = "stores/catalog-v3/products-v3/query-products";
const resource = lightIndex.find(r =>
r.docsUrl.includes(partial) || r.methods.some(m => m.docsUrl?.includes(partial)));
if (!resource) return "No API resource found for this partial URL";
const methodDocsUrl = resource.methods.find(m => m.docsUrl?.includes(partial))?.docsUrl;
if (!methodDocsUrl) return { message: "Resource found, no matching method", methods: resource.methods.map(m => m.docsUrl) };
const method = (await getResourceSchemaByUrl(methodDocsUrl)).methods[0]; // canonical method URL → scoped
return { title: method.summary, publicUrl: method.publicUrl, httpMethod: method.httpMethod.toUpperCase(), requestBody: method.requestBody, responses: method.responses };
}Expand selected nested $circular types (targeted — resolve only what you need):
async function() {
const methodUrl = "https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products";
const schema = await getResourceSchemaByUrl(methodUrl);
const method = schema.methods[0]; // method URL → scoped result
return {
requestBody: method.requestBody,
selectedNestedTypes: {
product: schema.components.schemas["com.wix.stores.catalog.product.api.v3.Product"],
cursorPaging: schema.components.schemas["wix.stores.catalog.v3.upstream.wix.common.CursorPaging"]
}
};
}Advanced — bounded recursive expansion (only when top-level + selected refs aren’t enough; keep depth small, schemas balloon fast):
async function() {
const methodUrl = "https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products";
const schema = await getResourceSchemaByUrl(methodUrl);
const method = schema.methods[0]; // method URL → scoped result
function expand(value, depth = 0, seen = []) {
if (depth > 3) return value;
if (Array.isArray(value)) return value.map(v => expand(v, depth, seen));
if (!value || typeof value !== "object") return value;
if (value.$circular) {
const name = value.$circular;
if (seen.includes(name)) return { $ref: name, circular: true };
const target = schema.components?.schemas?.[name];
return target ? { $ref: name, schema: expand(target, depth + 1, seen.concat(name)) } : { $ref: name, missing: true };
}
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, expand(v, depth, seen)]));
}
return { title: method.summary, publicUrl: method.publicUrl, requestBody: expand(method.requestBody), responses: expand(method.responses) };
}SKILL.md (semantic doc-search, .md twin/browse).publicUrls) → lightIndex, here.getResourceSchema[ByUrl], here.SearchWixAPISpec → getResourceSchemaByUrl (same data, native tool).Always confirm the endpoint, HTTP verb, and body shape here before writing the call — never guess.