Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

Product Catalog Validator

commerce.catalog_validate

ProductionBounded custom (Class B)Deterministicstrictfastv1.0.0
No network

Overview

You send products and their variants, and we report the domain problems: a SKU used twice, a variant whose option value the product never declared, a declared combination with no variant behind it, a variant priced in a different currency from its product, a required field left blank, a parentId that does not resolve, a cycle, or a grandchild in a catalogue that only allows one level. A malformed request fails; a badly modelled catalogue produces issues — reporting them is the job, so we do not refuse the catalogue we were asked to inspect. Every issue carries a JSON Pointer into the document you sent, so you navigate to the problem instead of searching for it. There is no schema input: structural conformance belongs to schema.validate_json, and this skill does no JSON-Schema validation at all.

The problems that break a storefront are rarely structural. The JSON is well formed and the schema passes, and yet two products share a SKU, a variant carries a colour the product never declared, half the size matrix is missing, and one product's parent was deleted last month. Those are relationships across the document, which a schema cannot express, and this skill exists to check exactly them — and to point at each one with a pointer you can follow.

Reliability

Fully deterministic: the same catalogue under the same rules produces the same issues, in the same order — product by product in document order, with parent problems last — so two runs can be compared line by line. The report is bounded: issues stop at rules.maxIssues while the counts keep counting, so summary.issueCount can exceed the array and truncated says so. A catalogue with problems is a successful call: only a malformed request fails. The line between the two is attribution, not severity: every issue is attributed to a product id, so a repeated id means the report could not say which product a problem belongs to and the call fails, while a repeated SKU is attributable and is therefore reported as SKU_DUPLICATE rather than refused. A property this skill does not implement is refused, not ignored: an unknown top-level property is a broken request and comes back as the platform's TOOL_INPUT_INVALID, one on a product or variant is CATALOG_INVALID, and one in rules is CATALOG_RULES_INVALID. Failures, and what to do about each. CATALOG_INVALID — trigger: a product has no id, has a blank one, one longer than 128 characters, or repeats one; a product or variant carries an unknown property; options, media or optionValues are the wrong shape; or a product declares more than 8 options, more than 100 values for one, or more than 200 media entries; retryable: no, the same request always fails; next: fix the shape at the path we name. Note that a badly modelled catalogue is not this — it comes back as issues. CATALOG_LIMIT_EXCEEDED — trigger: more than 2000 products, or a COMPLETE matrix with more than 20000 combinations for one product — which needs several options multiplied together, since one option is capped at 100 values and 101 of them is CATALOG_INVALID first; retryable: no; next: validate in batches, or use SUBSET for the product with the enormous matrix. CATALOG_VARIANT_LIMIT_EXCEEDED — trigger: more than 200 variants on one product, or more than 20000 in the call; retryable: no; next: split the catalogue, or model the extra variants as a separate product. CATALOG_RULES_INVALID — trigger: a rule names a field that does not exist on its shape, requiredProductFields or requiredVariantFields is an empty array, an enum is outside its values, maxIssues is outside 1 to 10000, or rules carries a property we do not implement; retryable: no; next: fix the rule we name.

Context footprint

The report is bounded by rules.maxIssues, default 1000, so a systematically broken 20000-variant catalogue answers with 1000 issues and an honest total rather than 20000 entries. Three ceilings bound a request — 2000 products, 20000 variants, and 1 MiB — and the counts are reachable inside the bytes when entries are minimal: 2001 bare products is about 30 KB and 20001 bare variants about 700 KB. A realistic catalogue is far heavier per entry, so a full one meets the 1 MiB request ceiling long before either count, and is then refused for size rather than answered; send it in batches. A successful call costs 1 execution credit.

When to use it

Before a catalogue import, a launch, or a channel export, when you need to know whether the data is coherent and not merely well formed.

When the question is structural conformance to a schema, or when you want the data cleaned rather than reported on.

Scope

Note
Report the five families of catalogue domain problems — duplicate SKUs, option-matrix integrity, price and currency coherence, required fields, and parent/child integrity — against a catalogue supplied in the request.

Non-goals

  • Does not validate against a JSON Schema, and takes no schema input — schema.validate_json is the platform's one JSON-Schema validator
  • Does not fetch the catalogue from anywhere; it checks what you send
  • Does not fix, normalise or deduplicate anything
  • Does not check stock, publish, or change a product's status
  • Does not validate a SKU's format — commerce.sku_normalize is that skill
  • Does not check prices against a market or a competitor

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

validation

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input1 MB

How we handle your data

Sensitive output possible
What this means

This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response may contain submitted or transformed content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
catalogobjectYesClosed catalog object with bounded products, variants, categories, and currency properties.
rulesobjectNoClosed catalog-rule object; omit it to apply the documented safe defaults. Its documented commercial properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.

Output schema

FieldTypeRequiredDescription
validbooleanYesWhether no error-severity catalog issue exists.
issuesarrayYesBounded array of catalog issue receipts with severity, code, path, and message. The item shape is defined by the nested schema and the array contains from 0 through 100000 items.
summaryobjectYesClosed product, variant, category, error, and warning count summary. Its documented commercial properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.
rulesobjectYesClosed resolved catalog rules with defaults filled in. Its documented commercial properties are bounded to 256 entries — unconstrained RFC 8259 JSON items.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"commerce.catalog_validate","input":{"catalog":{"products":[{"id":"P-100","title":"Basic T-Shirt","price":"19.90","currency":"EUR"}]}}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("commerce.catalog_validate", {"catalog":{"products":[{"sku":"TSHIRT-BASE","media":["https://cdn.example.com/tshirt.jpg"],"status":"ACTIVE","currency":"EUR","options":[{"values":["S","M"],"name":"size"},{"values":["black"],"name":"colour"}],"price":"19.90","title":"Basic T-Shirt","id":"P-100","variants":[{"id":"V-1","optionValues":{"colour":"black","size":"S"},"sku":"TSHIRT-S-BLACK","price":"19.90","currency":"EUR"},{"id":"V-2","optionValues":{"colour":"black","size":"M"},"sku":"TSHIRT-M-BLACK","price":"19.90","currency":"EUR"}]}]}});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
CATALOG_INVALID400NoThe request payload failed this skill's input validation.
CATALOG_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
CATALOG_VARIANT_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.
CATALOG_RULES_INVALID400NoThe request payload failed this skill's input validation.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not validate against a JSON Schema, and takes no schema input — schema.validate_json is the platform's one JSON-Schema validator
  • Does not fetch the catalogue from anywhere; it checks what you send
  • Does not fix, normalise or deduplicate anything
  • Does not check stock, publish, or change a product's status
  • Does not validate a SKU's format — commerce.sku_normalize is that skill
  • Does not check prices against a market or a competitor
  • Checking that a document conforms to a JSON Schema
  • Normalising or repairing product data
  • Deciding what a product should be priced at

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "commerce.catalog_validate",
  "input": {
    "catalog": {
      "products": [
        {
          "sku": "TSHIRT-BASE",
          "media": [
            "https://cdn.example.com/tshirt.jpg"
          ],
          "status": "ACTIVE",
          "currency": "EUR",
          "options": [
            {
              "values": [
                "S",
                "M"
              ],
              "name": "size"
            },
            {
              "values": [
                "black"
              ],
              "name": "colour"
            }
          ],
          "price": "19.90",
          "title": "Basic T-Shirt",
          "id": "P-100",
          "variants": [
            {
              "id": "V-1",
              "optionValues": {
                "colour": "black",
                "size": "S"
              },
              "sku": "TSHIRT-S-BLACK",
              "price": "19.90",
              "currency": "EUR"
            },
            {
              "id": "V-2",
              "optionValues": {
                "colour": "black",
                "size": "M"
              },
              "sku": "TSHIRT-M-BLACK",
              "price": "19.90",
              "currency": "EUR"
            }
          ]
        }
      ]
    }
  }
}

Examples

A clean two-variant product under the default rules

Inputjson
{
  "catalog": {
    "products": [
      {
        "sku": "TSHIRT-BASE",
        "media": [
          "https://cdn.example.com/tshirt.jpg"
        ],
        "status": "ACTIVE",
        "currency": "EUR",
        "options": [
          {
            "values": [
              "S",
              "M"
            ],
            "name": "size"
          },
          {
            "values": [
              "black"
            ],
            "name": "colour"
          }
        ],
        "price": "19.90",
        "title": "Basic T-Shirt",
        "id": "P-100",
        "variants": [
          {
            "id": "V-1",
            "optionValues": {
              "colour": "black",
              "size": "S"
            },
            "sku": "TSHIRT-S-BLACK",
            "price": "19.90",
            "currency": "EUR"
          },
          {
            "id": "V-2",
            "optionValues": {
              "colour": "black",
              "size": "M"
            },
            "sku": "TSHIRT-M-BLACK",
            "price": "19.90",
            "currency": "EUR"
          }
        ]
      }
    ]
  }
}
Expected outputjson
{
  "valid": true,
  "summary": {
    "productCount": 1,
    "variantCount": 2,
    "issueCount": 0,
    "bySeverity": {},
    "truncated": false
  },
  "rules": {
    "requiredProductFields": [
      "id",
      "title",
      "price",
      "currency"
    ],
    "requiredVariantFields": [
      "id",
      "sku",
      "optionValues"
    ],
    "skuUniqueness": "GLOBAL",
    "optionMatrix": "SUBSET",
    "requireMedia": false,
    "priceCoherence": true,
    "maxIssues": 1000
  },
  "issues": []
}

A catalogue with a duplicate SKU, a currency mismatch, an undeclared option value, a gap in a COMPLETE matrix, a missing title, a negative price and an unresolved parent

Inputjson
{
  "rules": {
    "optionMatrix": "COMPLETE"
  },
  "catalog": {
    "products": [
      {
        "title": "Blue Mug",
        "id": "P-100",
        "price": "9.90",
        "options": [
          {
            "values": [
              "S",
              "M"
            ],
            "name": "size"
          }
        ],
        "currency": "EUR",
        "sku": "MUG-BLUE",
        "variants": [
          {
            "sku": "MUG-BLUE-S",
            "optionValues": {
              "size": "S"
            },
            "currency": "EUR",
            "id": "V-1"
          },
          {
            "sku": "MUG-BLUE-S",
            "optionValues": {
              "size": "XL"
            },
            "currency": "USD",
            "id": "V-2"
          }
        ]
      },
      {
        "id": "P-200",
        "parentId": "P-999",
        "sku": "MUG-RED",
        "price": "-3.00",
        "currency": "EUR"
      }
    ]
  }
}
Expected outputjson
{
  "valid": false,
  "summary": {
    "productCount": 2,
    "variantCount": 2,
    "issueCount": 7,
    "bySeverity": {
      "ERROR": 7
    },
    "truncated": false
  },
  "rules": {
    "requiredProductFields": [
      "id",
      "title",
      "price",
      "currency"
    ],
    "requiredVariantFields": [
      "id",
      "sku",
      "optionValues"
    ],
    "skuUniqueness": "GLOBAL",
    "optionMatrix": "COMPLETE",
    "requireMedia": false,
    "priceCoherence": true,
    "maxIssues": 1000
  },
  "issues": [
    {
      "code": "SKU_DUPLICATE",
      "severity": "ERROR",
      "path": "/catalog/products/0/variants/1/sku",
      "productId": "P-100",
      "variantId": "V-2",
      "detail": "This SKU already appears earlier in the catalogue, and rules.skuUniqueness is GLOBAL."
    },
    {
      "code": "CURRENCY_MISMATCH",
      "severity": "ERROR",
      "path": "/catalog/products/0/variants/1/currency",
      "productId": "P-100",
      "variantId": "V-2",
      "detail": "The variant declares a different currency from its product, so one product would be priced in two currencies."
    },
    {
      "code": "OPTION_VALUE_UNDECLARED",
      "severity": "ERROR",
      "path": "/catalog/products/0/variants/1/optionValues/size",
      "productId": "P-100",
      "variantId": "V-2",
      "detail": "The variant's value for option 'size' is not among the values the product declares for it."
    },
    {
      "code": "OPTION_COMBINATION_MISSING",
      "severity": "ERROR",
      "path": "/catalog/products/0/variants",
      "productId": "P-100",
      "detail": "No variant exists for the declared combination size=M."
    },
    {
      "code": "REQUIRED_FIELD_MISSING",
      "severity": "ERROR",
      "path": "/catalog/products/1/title",
      "productId": "P-200",
      "detail": "Required product field 'title' is missing or blank."
    },
    {
      "code": "PRICE_INVALID",
      "severity": "ERROR",
      "path": "/catalog/products/1/price",
      "productId": "P-200",
      "detail": "The price is negative."
    },
    {
      "code": "PARENT_UNRESOLVED",
      "severity": "ERROR",
      "path": "/catalog/products/1/parentId",
      "productId": "P-200",
      "detail": "parentId names a product that is not in this catalogue."
    }
  ]
}
  • Gate a catalogue import before it reaches your storefront
  • Find the variant that was quietly created with an option value nobody declared
  • Prove a size-and-colour matrix is complete before a launch

Avoid for

  • Checking that a document conforms to a JSON Schema
  • Normalising or repairing product data
  • Deciding what a product should be priced at

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

datacatalogvalidationcommerce