Platforms

Shopify

Connect a Shopify store to manage its catalog, read its orders, publish blog articles and install ad pixels on the storefront.

Connecting Shopify gives you a store account, which is a different kind of account from a social one. Through it you run the catalog (products, stock, collections, discounts), read orders, publish blog articles and install ad pixels on the storefront. You cannot pick a store as a destination in POST /v1/posts, and it has no inbox or social analytics.

#What you can do

Job How Page
Manage products, variants, images and stock Commerce API (/v1/commerce/...) Products
Read orders with line items, tax, refunds and attribution Commerce API, read-only Orders
Collections, discounts, pages, menus, redirects, markets, metafields Commerce API Commerce overview
Keep a Meta product catalog in sync for catalog ads Catalog syncs Products
Create, schedule and edit blog articles Blogs API Blogs
Fire Meta, TikTok, Google, LinkedIn, Pinterest, X and ChatGPT pixels Tracking tag install Site pixels
Property Value
Platform id shopify
Connect OAuth install (needs the store's myshopify.com domain) or a custom-app Admin token
Social posting No
Analytics No (order data comes from the Commerce API)
Ids Shopify's own numeric ids as strings. Read them from responses, never build them

#Connect a store

Shopify cannot look up a merchant's stores for you, so you need the store domain first. Ask the merchant for it (acme.myshopify.com, or just acme).

curl "https://api.pozy.ai/v1/connect/shopify?profileId=6710c3a9e4b0d2f1a8c95e21&shop=acme.myshopify.com&redirect_url=https://app.example.com/connected" \
  -H "Authorization: Bearer $POZY_API_KEY"

Open authUrl in the merchant's browser. When they approve the install, Shopify and Pozy send them to your redirect_url, whose query string holds connected, profileId, accountId and username, the store's domain. Keep the accountId. Rely on the redirect rather than on the domain you sent: the merchant can end up approving another store.

redirect_url has to be a full http(s) URL or a custom app scheme like myapp://connected; a relative path gets 400. If you connect a store again to the profile it is already in, Pozy just renews the existing account's token.

#Connect with an Admin token instead

Without a browser step, the merchant sets up a custom app in the Shopify admin under Settings > Apps and sales channels > Develop apps. They give it read_content, write_content, read_products and write_products, then hand you its Admin API token, which begins with shpat_.

cURL
curl -X POST https://api.pozy.ai/v1/connect/shopify/token \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6710c3a9e4b0d2f1a8c95e21",
    "shop": "acme.myshopify.com",
    "accessToken": "shpat_..."
  }'

Pozy checks the token against the store before it saves anything. Custom-app tokens do not expire. Two things work differently with a token: the store cannot grant optional permissions through Pozy (the merchant adds scopes to the custom app instead), and pixels cannot be installed, because a Shopify web pixel belongs to the app that installs it.

#Permissions

The install asks for the scopes Pozy needs for content and catalog work. Nothing about customers or payments is requested up front.

Scope Enables
read_content, write_content Blogs, articles and online store pages
read_products, write_products Products, variants, images, collections and metafields
write_pixels, read_customer_events Installing pixels and letting them receive storefront events

Stock, sales channels, discounts, navigation, metaobjects, markets, marketing activities, image removal and orders need optional scopes. The merchant approves those later without reinstalling. Call GET /v1/commerce/store: it lists what the store can do now in capabilities, what is missing in missingCapabilities, and returns a grantPermissionsUrl to send the store owner to.

Capability Shopify scope behind it
products.images_remove write_files
inventory.read, inventory.write write_inventory and read_locations
channels.read, channels.write write_publications
discounts.read, discounts.write, discounts.codes write_discounts
navigation.read, navigation.write write_online_store_navigation
metaobjects.read, metaobjects.write write_metaobjects
markets.read, markets.write write_markets (writes also use write_products)
marketing.write write_marketing_events
orders.read read_orders and read_all_orders
Orders need two scopes

Without read_all_orders, Shopify only returns the last 60 days of orders, so a store that granted only read_orders still lists orders.read as missing. Order reads also need Shopify's approval for protected customer data. Where that approval does not cover buyer details, customer, billingAddress and shippingAddress come back as null.

#Shopify-only operations

Most of the Commerce API works on both Shopify and WooCommerce. These operations exist only on Shopify and return 400 platform_not_supported elsewhere:

  • Sales channels: list channels, publish or unpublish products and collections
  • Markets and price lists, including fixed prices per market
  • Metaobjects and their definitions
  • Navigation menus and URL redirects
  • Collection metafields and reordering products inside a collection
  • Adding codes to an existing discount
  • Marketing activities: log a post, ad or message you ran for the merchant so it shows in the store's Marketing area with its UTM tags, then add engagement numbers to it each day
cURL
curl -X PUT https://api.pozy.ai/v1/commerce/marketing-activities \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "6710c4b2e4b0d2f1a8c95e3a",
    "remoteId": "post_6711a0c3e4b0d2f1a8c96001",
    "title": "Autumn drop on Instagram",
    "url": "https://acme.com/collections/autumn",
    "utm": { "campaign": "autumn-drop", "source": "instagram", "medium": "social" },
    "tactic": "post",
    "channel": "social",
    "status": "active"
  }'

Set remoteId to an id from your side, such as the Pozy post id. A second call with that remoteId changes the existing activity instead of adding one.

#Limits and behaviour

  • Pozy keeps no copy of store data. Every read goes to the store, except the catalog syncs you create.
  • Shopify tells Pozy about product changes through its webhooks, so synced catalogs catch up within minutes and you get commerce.product.* events.
  • When the merchant uninstalls the app, Shopify revokes the token at once and Pozy marks the account disconnected. There is nothing for you to revoke. Reinstalling runs the normal connect flow and issues a fresh token.
  • Blog and article deletes are permanent. Deleting a blog deletes every article in it.
  • You cannot create orders, write customers, upload media or publish social posts through a store account.

#Errors

Error Cause Fix
400 on connect shop is not a myshopify.com domain, or redirect_url is relative Pass acme.myshopify.com (or acme) and an absolute redirect
403 insufficient_permissions The store never granted the needed scope, or Shopify turned the call down Point the owner to grantPermissionsUrl, or connect the store again
409 reconnect_required A pixel install on a store connected with a token or without pixel access Send the merchant to details.authUrl. The account keeps its id
404 product_not_found The product was deleted or belongs to another store List products for a current id
404 blog_article_not_found The article was deleted or is in another blog List the blog's articles
429 Your Pozy rate limit or Shopify's own limit Wait for Retry-After, then retry

#Endpoints

Endpoint What it does
GET /v1/connect/shopify Get Shopify OAuth connect URL
POST /v1/connect/shopify/token Connect a Shopify store with a custom-app Admin token
GET /v1/commerce/store Get a store

The full catalog, order and discount surface is listed on Commerce, and the article endpoints on Blogs.

#Webhook events

Event When it fires
account.connected Account connected.
account.disconnected Account disconnected.
commerce.product.created Commerce product created.
commerce.product.updated Commerce product updated.
commerce.product.deleted Commerce product deleted.
commerce.order.created Commerce order created.
commerce.order.updated Commerce order updated.
commerce.order.refunded Commerce order refunded.