Commerce & blogs

Products and catalog

List, create and edit products, variants, images and stock on Shopify and WooCommerce, organise collections and discounts, and sync a store into a Meta catalog.

The Commerce API manages a connected store's catalog in one platform-neutral shape. The same calls create a product on Shopify or WooCommerce, change its stock, put it in a collection, discount it and keep a Meta catalog in step for catalog ads. Name the store with accountId on every call.

#List products

query goes to the platform's own product search unchanged (on Shopify, for example, vendor:Acme tag:summer). collectionId narrows the list to one collection. Pages hold up to 100 products, 20 by default.

curl "https://api.pozy.ai/v1/commerce/products?accountId=6710c4b2e4b0d2f1a8c95e3a&status=active&limit=50" \
  -H "Authorization: Bearer $POZY_API_KEY"

Each product carries its variants (with price, compareAtPrice, sku, inventoryQuantity and options), images, featuredImage, options, tags, seo, the public storefront url and totalInventory. A status the platform has no equivalent for returns an empty page rather than an error.

#Create a product

A new product starts as draft unless you send status: "active". It takes up to 3 options, 100 variants and 20 images. Image URLs must be public: the store downloads them.

curl -X POST https://api.pozy.ai/v1/commerce/products \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6710c4b2e4b0d2f1a8c95e3a",
    "title": "Merino beanie",
    "descriptionHtml": "<p>Soft merino wool, one size.</p>",
    "status": "active",
    "tags": ["winter", "accessories"],
    "images": [{ "url": "https://cdn.example.com/beanie-grey.jpg", "altText": "Grey merino beanie" }],
    "options": [{ "name": "Colour", "values": ["Grey", "Navy"] }],
    "variants": [
      { "sku": "BEANIE-GRY", "price": "29.00", "options": [{ "name": "Colour", "value": "Grey" }] },
      { "sku": "BEANIE-NVY", "price": "29.00", "options": [{ "name": "Colour", "value": "Navy" }] }
    ]
  }'

#Edit products

Change Endpoint Notes
Title, description, handle, tags, SEO, vendor, type PATCH /v1/commerce/products/{productId} tags replaces the whole list
Variant prices and compare-at prices POST /v1/commerce/products/{productId}/price Send compareAtPrice: null to end a sale
Add or delete variants and options .../variants, .../options
Add, remove or reorder images .../images, .../images/reorder Removal on Shopify needs products.images_remove
Copy a product POST .../duplicate New title, starts as draft, includeImages defaults to true
Tags on many products POST /v1/commerce/products/tags add and remove lists, per-product results
Activate, deactivate, archive or delete POST /v1/commerce/products/state Up to 50 ids, per-product results
Custom fields PUT, GET, DELETE .../metafields Metafields on Shopify, custom fields on WooCommerce
Sales channels (Shopify) POST .../channels publish and unpublish lists of channel ids

Bulk calls report each outcome in succeeded and failed, so one bad id does not stop the rest:

Response from products/state
{
  "action": "archive",
  "succeeded": ["8214569812345"],
  "failed": [{ "productId": "8214569899999", "message": "Product not found" }]
}
Deletes are permanent

action: "delete" removes the product on the platform and cannot be undone. Archive it if you might need it again.

#Set stock

Read location ids with GET /v1/commerce/locations (WooCommerce has one, default), then change variants. mode: "set" makes quantity the new available count. mode: "adjust" adds it, so send a negative number to subtract.

curl -X POST https://api.pozy.ai/v1/commerce/products/8214569812345/inventory \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6710c4b2e4b0d2f1a8c95e3a",
    "mode": "adjust",
    "changes": [{ "variantId": "45123456789012", "locationId": "71234567890", "quantity": -3 }]
  }'

The response is the product's stock after the change, per location: available, onHand, committed and incoming. Read it any time with GET /v1/commerce/inventory?accountId=...&productId=.... On WooCommerce an adjust reads then writes, so a sale that lands in between can be overwritten.

#Collections and discounts

Collections group products (Shopify collections, WooCommerce categories). Create one with a list of productIds, then add or remove products with POST /v1/commerce/collections/{collectionId}/products and add / remove lists. sortOrder and SEO fields are Shopify-only.

Discounts take a method (code or automatic) and a type (percentage, fixed_amount or free_shipping):

cURL
curl -X POST https://api.pozy.ai/v1/commerce/discounts \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6710c4b2e4b0d2f1a8c95e3a",
    "title": "Instagram followers 15%",
    "method": "code",
    "type": "percentage",
    "code": "INSTA15",
    "percentage": 15,
    "oncePerCustomer": true,
    "endsAt": "2026-12-31T23:59:59Z"
  }'

Pair a code with a post: create the discount, then publish the code in a caption. WooCommerce supports code discounts (coupons) only, cannot start one in the future and cannot require a minimum quantity. Activate or pause a discount with POST /v1/commerce/discounts/{discountId}/state.

#Sync a store into a Meta catalog

A catalog sync keeps a Meta product catalog in step with the store, for catalog ads and shops. You need:

  • accountId: the store.
  • catalogAccountId: a connected facebook, instagram or metaads account whose Meta login can manage catalogs. WhatsApp accounts do not work here.
  • catalogId: the Meta catalog id, from GET /v1/ads/catalogs.
curl -X POST https://api.pozy.ai/v1/commerce/catalog-syncs \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6710c4b2e4b0d2f1a8c95e3a",
    "catalogAccountId": "6710c52de4b0d2f1a8c95e95",
    "catalogId": "1188234455667788"
  }'

The call returns 202 and starts the first full run. Poll GET /v1/commerce/catalog-syncs/{syncId}:

Field Meaning
runStatus pending, running, then succeeded or failed
itemsSent Variants Meta accepted in the last run
itemsSkipped Active products left out because they are unpublished or have no image
itemsDeleted This store's items removed because they are gone or no longer listable
lastError Why the last run failed, or a note on rejected items

How a sync behaves:

  • One catalog item per variant, grouped by product, with ids namespaced to the store. A run only touches that store's items, so one catalog can hold several stores.
  • A product is listable when it is active, published to the online store and has at least one image.
  • Product changes refresh their items within minutes. Every sync also runs once a day if it has not run in the last 20 hours. A run still going after 30 minutes counts as failed and is retried on the next daily pass.
  • Run now with POST .../run (409 catalog_sync_conflict while a run is in progress). Stop with DELETE: items already in the catalog stay.
  • One sync per store and catalog pair. Syncs of a disconnected store are skipped until it reconnects.

Then build catalog ads on top: see Catalogs.

#Use products in posts

Products give your posts real data. A common pattern: list active products, pick the newest, and create a post with its first image and storefront url, tagged with UTM parameters. On Shopify, record the post as a marketing activity so the merchant sees it in their Marketing section (see Shopify).

#Webhook events

Pozy subscribes to the store's product changes when it connects. The events carry the store and the product id; fetch the product with GET /v1/commerce/products/{productId} for its current state.

Event When it fires
commerce.product.created Commerce product created.
commerce.product.updated Commerce product updated.
commerce.product.deleted Commerce product deleted.

#Endpoints

Endpoint What it does
GET /v1/commerce/products List products
POST /v1/commerce/products Create a product
POST /v1/commerce/products/state Activate, deactivate, archive or delete products
GET /v1/commerce/products/{productId} Get a product
PATCH /v1/commerce/products/{productId} Update a product
POST /v1/commerce/products/{productId}/price Update variant prices
GET /v1/commerce/collections List collections
POST /v1/commerce/collections Create a collection
GET /v1/commerce/collections/{collectionId} Get a collection
PATCH /v1/commerce/collections/{collectionId} Update a collection
DELETE /v1/commerce/collections/{collectionId} Delete a collection
POST /v1/commerce/collections/{collectionId}/products Add or remove products in a collection
POST /v1/commerce/products/{productId}/variants Add variants
DELETE /v1/commerce/products/{productId}/variants Delete variants
POST /v1/commerce/products/{productId}/options Add options
DELETE /v1/commerce/products/{productId}/options Delete options
POST /v1/commerce/products/{productId}/images Add images
DELETE /v1/commerce/products/{productId}/images Remove images
POST /v1/commerce/products/{productId}/images/reorder Reorder images
POST /v1/commerce/products/{productId}/duplicate Duplicate a product
GET /v1/commerce/products/{productId}/metafields List product metafields
PUT /v1/commerce/products/{productId}/metafields Set product metafields
DELETE /v1/commerce/products/{productId}/metafields Delete product metafields
POST /v1/commerce/products/{productId}/channels Publish or unpublish a product
GET /v1/commerce/collections/{collectionId}/metafields List collection metafields
PUT /v1/commerce/collections/{collectionId}/metafields Set collection metafields
DELETE /v1/commerce/collections/{collectionId}/metafields Delete collection metafields
POST /v1/commerce/collections/{collectionId}/channels Publish or unpublish a collection
POST /v1/commerce/products/tags Add or remove tags in bulk
POST /v1/commerce/collections/{collectionId}/reorder Reorder products in a collection
GET /v1/commerce/locations List locations
GET /v1/commerce/inventory Get a product's stock
POST /v1/commerce/products/{productId}/inventory Set or adjust stock
GET /v1/commerce/channels List sales channels
GET /v1/commerce/discounts List discounts
POST /v1/commerce/discounts Create a discount
GET /v1/commerce/discounts/{discountId} Get a discount
PATCH /v1/commerce/discounts/{discountId} Update a discount
DELETE /v1/commerce/discounts/{discountId} Delete a discount
POST /v1/commerce/discounts/{discountId}/state Activate or deactivate a discount
POST /v1/commerce/discounts/{discountId}/codes Add codes to a discount
GET /v1/commerce/catalog-syncs List catalog syncs
POST /v1/commerce/catalog-syncs Sync a store into a Meta catalog
GET /v1/commerce/catalog-syncs/{syncId} Get a catalog sync
DELETE /v1/commerce/catalog-syncs/{syncId} Stop a catalog sync
POST /v1/commerce/catalog-syncs/{syncId}/run Run a catalog sync now

The older per-account product endpoints (/v1/accounts/{accountId}/products) still work but are deprecated. Use the Commerce API for new code.