Zernio
Zernio
Overview

Social Posting

XInstagramFacebookLinkedInTikTokYouTubePinterestRedditBlueskyThreadsGoogle Business ProfileSnapchat

Messaging

WhatsAppRCSiMessageTelegramDiscordSlack

Telephony

Phone NumbersVoice & CallsSMS

Advertising

Meta AdsGoogle AdsLinkedIn AdsTikTok AdsPinterest AdsX AdsOpenAI Ads

Blogs & Commerce

ShopifyWordPress
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

Shopify

Connect a Shopify store to manage its blog articles through the Blogs API, manage its catalog, stock and discounts through the Commerce API and install ad platform pixels; a store publishes no social posts.


Create, schedule, update and delete the blog articles of a Shopify store with the Blogs API, manage its products, stock, collections, discounts and more with the Commerce API, and install Meta, TikTok, Google, LinkedIn, Pinterest, X and OpenAI tags on its storefront (Tracking pixels), through a connected account with platform: "shopify". Shopify is connect-only: a store never appears as a target in POST /v1/posts, publishes no social posts and reports no analytics.

Quick reference

PropertyValue
Platform valueshopify
What it managesStorefront blogs and blog articles; the store's catalog through the Commerce API
AuthOAuth 2.0 (store domain required) or a custom-app Admin token
Scopesread_content, write_content, read_products, write_products, write_pixels, read_customer_events
Tracking pixelsOne Zernio web pixel firing one tag per ad platform (Tracking pixels)
Social postingNo
AnalyticsNo
Media requirementsNone (article images are referenced by URL)
SchedulingYes, native (Shopify publishes at publishDate; no Zernio queue)
DraftsYes (isPublished: false)
Article bodyHTML (bodyHtml)
SEO fieldsYes (seo.title, seo.description)
PaginationCursor (limit 1 to 50, default 20, plus nextCursor)

Before you start

Shopify requires the store's myshopify.com domain before OAuth can start. Shopify has no store picker and no lookup from a merchant to their shops, so an authorization URL can only be built for a domain you already know; collect it from the merchant first. A merchant who installs from the Shopify App Store never types it, because Shopify supplies the domain to Zernio on that path.

Blog, article, product and variant ids are Shopify's own numeric ids, not Zernio object ids. Read them from API responses; never construct them.

Connect

Call GET /v1/connect/shopify with profileId and shop on Get Shopify OAuth connect URL. Send the merchant to the returned authUrl; after they approve the install, Shopify calls Zernio's callback, the account is created on your profile, and the browser lands on your redirect_url. The connecting accounts guide covers the flow and scopes in general.

import Zernio from '@zernio/node';

const zernio = new Zernio();

const { data: connect } = await zernio.connect.getShopifyConnectUrl({
  query: {
    profileId: '66a1f0c2a4b9d3e8f1a2b3c4',
    shop: 'your-store.myshopify.com',
    redirect_url: 'https://myapp.com/connected'
  }
});
// Send the merchant's browser to connect.authUrl

Response (200):

{
  "authUrl": "https://your-store.myshopify.com/admin/oauth/authorize?client_id=...",
  "state": "..."
}

shop accepts the full domain or the bare your-store prefix. redirect_url must be an absolute http(s) URL or an app scheme such as myapp://callback; a relative path is rejected with 400 INVALID_REDIRECT_URL. Connecting the same profile to the same store again refreshes the stored token in place instead of creating a second account.

Custom-app Admin token

To skip the browser flow, the merchant creates a custom app in their Shopify admin (Settings, then Apps and sales channels, then Develop apps) with the read_content, write_content, read_products and write_products scopes and hands you its Admin API access token, which starts with shpat_. Exchange it with Connect a Shopify store with a custom-app Admin token:

curl -X POST "https://zernio.com/api/v1/connect/shopify/token" \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "66a1f0c2a4b9d3e8f1a2b3c4",
    "shop": "your-store.myshopify.com",
    "accessToken": "shpat_..."
  }'

Response (200):

{
  "account": {
    "_id": "66b2e19d8c3f5a7e9d0b1c2d",
    "platform": "shopify",
    "username": "your-store.myshopify.com",
    "displayName": "Your Store",
    "profileId": "66a1f0c2a4b9d3e8f1a2b3c4"
  }
}

shop is required here too: an Admin token does not identify its own store. Zernio validates the token against the store before saving anything, and custom-app tokens do not expire.

OAuth scopes

ScopeWhat it enables
read_contentRead the store's blogs and articles
write_contentCreate, update and delete blogs and articles
read_productsList and read products, variants and images
write_productsEdit product fields and variant prices
write_pixelsInstall tracking pixels on the storefront
read_customer_eventsLet those pixels receive the store's customer events

Zernio requests no access to customers, orders or payments. Commerce features such as stock, discounts or markets use optional scopes that the merchant approves separately (see Commerce).

Stores connected before 16 September 2026 hold a content-only grant. On those, every Products endpoint answers 403 insufficient_permissions until the merchant goes through the connect flow again: the consent screen shows the two product scopes and the existing account keeps its id.

When the merchant uninstalls

Shopify invalidates its own token the moment the app is uninstalled and sends Zernio the app/uninstalled webhook, so there is nothing to revoke and nothing to call. Zernio deactivates that store's account: it stops appearing in GET /v1/accounts as active, and any request naming its accountId fails. Scheduled articles on the account are held through the disconnect grace period rather than deleted at once, so a reinstall inside that window brings them back with the account. Reinstalling runs the same connect flow and issues a fresh token.

Publish

There are no social posts. A connected store publishes blog articles: a store has one or more blogs (Shopify creates a "News" blog by default), and each blog holds articles. All content lives on Shopify; Zernio proxies it and stores nothing.

List blogs

Start by listing the blogs to get the id you will write into (List blogs):

curl "https://zernio.com/api/v1/accounts/66b2e19d8c3f5a7e9d0b1c2d/blogs" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Response (200):

{
  "platform": "shopify",
  "blogs": [
    { "id": "121793282419", "platform": "shopify", "title": "News", "handle": "news" }
  ],
  "nextCursor": null
}

Create an article

Call POST /v1/accounts/{accountId}/blogs/{blogId}/articles with title and bodyHtml (Create an article).

const { data: created } = await zernio.blogs.createBlogArticle({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', blogId: '121793282419' },
  body: {
    title: 'Autumn collection preview',
    bodyHtml: '<p>The first pieces land next month.</p>',
    tags: ['autumn', 'new-arrivals'],
    author: 'Maria Costa',
    excerpt: 'An early look at what is arriving this September.',
    isPublished: true
  }
});

console.log(created.article.id);

Response (201):

{
  "platform": "shopify",
  "article": {
    "id": "589234567890",
    "blogId": "121793282419",
    "platform": "shopify",
    "title": "Autumn collection preview",
    "handle": "autumn-collection-preview",
    "tags": ["autumn", "new-arrivals"],
    "isPublished": true,
    "publishedAt": "2026-09-08T09:00:00Z"
  }
}

Draft

isPublished: false keeps the article as a draft; it reads back with publishedAt: null:

{
  "title": "Autumn collection preview",
  "bodyHtml": "<p>The first pieces land next month.</p>",
  "isPublished": false
}

Scheduled article

A future publishDate schedules the article natively on Shopify. Shopify publishes it at that time with no Zernio queue involved, and until then the article reads back as isPublished: false with publishedAt set to the future date:

{
  "title": "Autumn collection preview",
  "bodyHtml": "<p>The first pieces land next month.</p>",
  "publishDate": "2027-01-01T12:00:00+01:00"
}

Featured image and SEO

image.url sets the featured image; Shopify downloads it, so the URL must be publicly reachable. seo.title and seo.description map to Shopify's title_tag and description_tag metafields, which themes read for the page title and meta description:

{
  "title": "Autumn collection preview",
  "bodyHtml": "<p>The first pieces land next month.</p>",
  "image": { "url": "https://cdn.example.com/autumn.jpg", "altText": "Wool coats on a rail" },
  "seo": { "title": "Autumn collection preview", "description": "An early look at the autumn pieces." }
}

Every operation

OperationEndpoint
List blogsGET /v1/accounts/{accountId}/blogs
Create a blogPOST /v1/accounts/{accountId}/blogs
Get a blogGET /v1/accounts/{accountId}/blogs/{blogId}
Update a blogPATCH /v1/accounts/{accountId}/blogs/{blogId}
Delete a blogDELETE /v1/accounts/{accountId}/blogs/{blogId}
List articlesGET /v1/accounts/{accountId}/blogs/{blogId}/articles
Create an articlePOST /v1/accounts/{accountId}/blogs/{blogId}/articles
Get an articleGET /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}
Update an articlePATCH /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}
Delete an articleDELETE /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId}

Commerce

A connected store is a Commerce store: the Commerce API at /v1/commerce creates and edits products, variants, images, stock, collections, discounts, sales channel publishing, market prices, metafields, metaobjects, pages, menus, redirects and marketing activities, and can sync the store into a Meta catalog for catalog ads. Pass this account's id as accountId.

Products, collections, metafields and pages work on every install. Stock, sales channels, discounts, navigation, metaobjects, markets, marketing activities and image removal need optional scopes: GET /v1/commerce/store lists them in missingCapabilities and returns grantPermissionsUrl, where the merchant approves them without reinstalling. See Capabilities and extra permissions.

curl "https://zernio.com/api/v1/commerce/store?accountId=66b2e19d8c3f5a7e9d0b1c2d" \
  -H "Authorization: Bearer $ZERNIO_API_KEY"

Products

The Products API below is deprecated in favor of the Commerce API, which returns the same catalog in the platform-neutral shape. It keeps working for existing integrations: list and read products with their variants, options and images, and edit a product's own fields and variant prices. All data lives on Shopify; Zernio proxies it and stores nothing.

List products

Call GET /v1/accounts/{accountId}/products. Filter with status (active, draft or archived) and query, Shopify's product search syntax, which is passed through verbatim:

const { data: page } = await zernio.products.listProducts({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d' },
  query: { status: 'active', query: 'vendor:Acme tag:summer', limit: 20 }
});

for (const product of page.products) {
  console.log(product.id, product.title, product.variants[0]?.price);
}

Response (200):

{
  "platform": "shopify",
  "products": [
    {
      "id": "8214569812345",
      "platform": "shopify",
      "title": "Linen shirt",
      "handle": "linen-shirt",
      "descriptionHtml": "<p>Breathable linen for warm days.</p>",
      "vendor": "Acme",
      "productType": "Shirts",
      "tags": ["summer", "linen"],
      "status": "active",
      "featuredImage": { "url": "https://cdn.shopify.com/s/files/1/.../linen-front.jpg", "altText": "Linen shirt, front" },
      "images": [{ "url": "https://cdn.shopify.com/s/files/1/.../linen-front.jpg", "altText": "Linen shirt, front" }],
      "options": [{ "name": "Size", "values": ["S", "M", "L"] }],
      "variants": [
        {
          "id": "45123456789012",
          "title": "S",
          "sku": "LINEN-S",
          "barcode": null,
          "price": "59.00",
          "compareAtPrice": "79.00",
          "inventoryQuantity": 12,
          "availableForSale": true,
          "selectedOptions": [{ "name": "Size", "value": "S" }]
        }
      ],
      "seo": { "title": "Linen shirt | Acme", "description": null },
      "totalInventory": 31,
      "onlineStoreUrl": "https://acme.com/products/linen-shirt",
      "createdAt": "2026-05-02T10:12:00Z",
      "updatedAt": "2026-09-01T08:30:00Z",
      "publishedAt": "2026-05-02T10:15:00Z"
    }
  ],
  "nextCursor": "eyJsYXN0X2lkIjo4MjE0NTY5ODEyMzQ1fQ=="
}

Pagination is the same cursor scheme as the Blogs API: limit 1 to 50 (default 20) and nextCursor, null on the last page. The response carries the first 100 variants and the first 20 images of each product.

Update a product

Call PATCH /v1/accounts/{accountId}/products/{productId} with any subset of title, descriptionHtml, handle, vendor, productType, tags, status, seo and variants. tags replaces the whole list. variants changes the price and compare-at price of the listed variant ids only; a variant that belongs to another product is a 400. The response is the product as it reads after the update.

const { data: updated } = await zernio.products.updateProduct({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', productId: '8214569812345' },
  body: {
    descriptionHtml: '<p>Breathable linen for warm days. Now in three sizes.</p>',
    tags: ['summer', 'linen', 'new'],
    seo: { title: 'Linen shirt | Acme', description: 'A breathable linen shirt in S, M and L.' },
    variants: [{ id: '45123456789012', price: 54.0, compareAtPrice: 79.0 }]
  }
});

console.log(updated.product.variants[0].price); // "54.00"

Prices are decimal strings in the store currency; a number is accepted and formatted to two decimals. Send "compareAtPrice": null to take a variant off sale. status: "draft" unpublishes a product without deleting it and status: "archived" hides it everywhere; both are reversible with status: "active".

Every product operation

OperationEndpoint
List productsGET /v1/accounts/{accountId}/products
Get a productGET /v1/accounts/{accountId}/products/{productId}
Update a productPATCH /v1/accounts/{accountId}/products/{productId}

Tracking pixels

Put an ad platform's pixel on the store's storefront and checkout with one call and no theme edits: POST /v1/accounts/{accountId}/tracking-tags/{tagId}/install, where accountId is your connected ads account (Meta, TikTok, Google, LinkedIn, Pinterest, X or OpenAI), tagId the tracking tag and the body names this store's storeAccountId. Zernio installs its web pixel app extension on the store, which maps Shopify's customer events to each platform's events.

How the install works

A Shopify app gets one web pixel per store, so the store runs a single Zernio web pixel that holds one tag per ad platform:

  • Install is idempotent. Calling it again rewrites the same settings; installing a different tag of the same platform replaces the previous one and names it in replacedTagId; tags of other platforms are kept.
  • Status (GET with ?storeAccountId=) says whether this tag is the one the store fires for its platform (installed), which tag of that platform it fires now (installedTagId), and lists every Zernio tag on the store in tags.
  • Uninstall (DELETE with ?storeAccountId=) removes only this platform's tag and keeps the others; the web pixel itself is deleted when no tag remains. Removing a tag that is not installed is a no-op, and removing a tag while the store fires a different tag of that platform changes nothing and answers 409 invalid_resource_state.
const { data } = await zernio.trackingtags.installTrackingTagOnStore({
  path: { accountId: '66b2e19d8c3f5a7e9d0b1c2d', tagId: '7412345678901234567' },
  body: { storeAccountId: '66b2e19d8c3f5a7e9d0b1c3e' }
});

Response (200), a TikTok Pixel added next to a Meta pixel already on the store:

{
  "platform": "tiktokads",
  "install": {
    "storeAccountId": "66b2e19d8c3f5a7e9d0b1c3e",
    "platform": "shopify",
    "shopDomain": "acme.myshopify.com",
    "tagPlatform": "tiktokads",
    "siteTagId": "D00IKHRC77UE0J0RTNHG",
    "installed": true,
    "installedTagId": "D00IKHRC77UE0J0RTNHG",
    "webPixelId": "2031620387",
    "tags": [
      { "platform": "metaads", "siteTagId": "1729525464415281" },
      { "platform": "tiktokads", "siteTagId": "D00IKHRC77UE0J0RTNHG" }
    ],
    "replacedTagId": null
  }
}

siteTagId, installedTagId, replacedTagId and tags carry the on-site id (the TikTok pixel code, the Google AW- id, the X or OpenAI pixel id), not the API tagId. Google, LinkedIn and X also store their per-event ids (conversion labels, rule ids, event ids) at install time, so install again after you add or change conversion events on those platforms.

Stores connected before 28 September 2026 have not granted write_pixels and read_customer_events. On those, all three calls answer 409 reconnect_required with a fresh consent link in details.authUrl and the missing scopes in details.missingScopes; the account keeps its id, so retry after the merchant approves. A store connected with a custom-app Admin token gets the same answer, because a web pixel belongs to the app that installs it. See When the store must re-approve for the full error.

Event mapping

The pixel subscribes to 7 Shopify standard customer events, reduces them to Zernio's neutral site events and sends each one to every installed platform:

Shopify eventSite eventMetaTikTokGoogleLinkedInPinterestXOpenAI
page_viewedpage_viewPageViewPageviewPAGE_VIEW action, if one existsPage view (base tag hit)pagevisitPage view (twq('config'))page_viewed
product_viewedview_contentViewContentViewContentNot sentVIEW_CONTENT rule, if one existsviewcontentCONTENT_VIEW event, if one existscontents_viewed
product_added_to_cartadd_to_cartAddToCartAddToCartADD_TO_CART action, if one existsADD_TO_CART rule, if one existsaddtocartADD_TO_CART event, if one existsitems_added
search_submittedsearchSearchSearchNot sentSEARCH rule, if one existssearchSEARCH event, if one existsCustom search
checkout_startedinitiate_checkoutInitiateCheckoutInitiateCheckoutBEGIN_CHECKOUT action, if one existsSTART_CHECKOUT rule, if one existsinitiatecheckoutCHECKOUT_INITIATED event, if one existscheckout_started
payment_info_submittedadd_payment_infoAddPaymentInfoAddPaymentInfoNot sentADD_BILLING_INFO rule, if one existsaddpaymentinfoADDED_PAYMENT_INFO event, if one existsCustom addpaymentinfo
checkout_completedpurchasePurchasePurchasePURCHASE action, if one existsPURCHASE rule, if one existscheckoutPURCHASE event, if one existsorder_created
  • Meta, TikTok, Pinterest and OpenAI receive every event. Product and cart events carry the variant id, value and currency; checkout events carry every line, the total and the currency; the purchase adds the order id.
  • Google counts only events that have their own conversion action, so it receives page_view, add_to_cart, initiate_checkout and purchase, each only when the account has an enabled WEBPAGE conversion action of the matching category with a label (the primary-for-goal action wins). Google has no category for search or payment info, and sending product views to the page-view action would count every product page twice. See Google tag.
  • LinkedIn always receives the page view as a base Insight Tag hit; any other event is sent only when the ad account has an enabled conversion rule of the matching type that a page can fire (an event-specific Insight Tag rule, not a Conversions API or URL-match rule; the most recently fired one wins), with the value, currency and order id. A KEY_PAGE_VIEW rule is never fired, since it would count every page as a conversion. See Insight Tag.
  • X always receives the page view; any other event is sent only when the ad account has a conversion event of the matching type (the most recently tracked one wins). See X Pixel.
  • OpenAI has no standard search or payment-info event, so those go out as the lowercase custom events search and addpaymentinfo, the names the OpenAI Conversions API stores for a server-side Search or AddPaymentInfo. Search text is never sent to OpenAI.

Deduplication

Every event carries an event id: Shopify's own event id, except the purchase, whose id is shopify_order_<orderId> (the numeric order id). Send a server-side purchase for the same order through POST /v1/ads/conversions with eventId: "shopify_order_<orderId>" and each platform counts it once. The id travels as Meta's eventID, TikTok's event_id, Google's oid (one conversion per order id per action), LinkedIn's eventId, Pinterest's event_id, X's conversion_id and OpenAI's event_id.

How events reach each platform

Shopify runs app pixels in a strict sandbox (a web worker with no DOM), where the platforms' own browser scripts cannot load. The Zernio pixel therefore sends each event straight to the endpoint the platform's script would call, and keeps the platform's click id (fbclid, ttclid, gclid/gbraid, li_fat_id, epik, twclid, oppref) in a first-party cookie so later events stay attributed:

PlatformEndpointStatus
Metawww.facebook.com/tr with _fbp / _fbcDocumented (the pixel's image endpoint)
TikTokanalytics.tiktok.com/api/v2/pixel, reusing _ttpUndocumented: the endpoint TikTok's events.js calls
Googlewww.googleadservices.com/pagead/conversion/<id>/ with the conversion labelDocumented no-JavaScript conversion tag; gclid/gbraid kept in _gcl_aw/_gcl_gb
LinkedInpx.ads.linkedin.com/collect with li_fat_id from the landing URLDocumented image pixel (pid, conversionId, eventId), plus the parameter names insight.min.js sends
Pinterestct.pinterest.com/v3/ (GET, or a form POST past 2048 characters)The endpoint Pinterest's documented noscript tag and core.js call, with core.js's parameter names; epik kept in _epik
Xanalytics.twitter.com/1/i/adsctReverse-engineered from uwt.js (X documents only the legacy image pixel)
OpenAIbzr.openai.com/v1/sdk/eventsDocumented image tag; oppref kept in __oppref for 30 days, amounts in minor units

The pixel follows the store's customer privacy settings: where consent is required, it fires only for visitors who allow marketing.

Platform fields

There is no platformSpecificData for Shopify, because a store is not a POST /v1/posts target. The article fields:

FieldTypeDefaultDescription
titlestringRequired.
bodyHtmlstringArticle body as HTML.
handlestring(slug of the title)URL slug. Shopify sets it once, at creation; renaming the article later leaves the URL unchanged, so set it explicitly when the URL matters.
tagsArray<string>Shopify returns them alphabetized, not in the order you sent.
authorstringDisplay name of the author.
excerptstringShort summary shown in blog listings.
image{url, altText?}Featured image, downloaded by Shopify from a public URL.
seo{title?, description?}Maps to Shopify's title_tag and description_tag metafields.
isPublishedbooleanfalse creates a draft.
publishDatedatetimeISO 8601 with offset or Z. A future date schedules publication on Shopify.

Media requirements

None. Article images are referenced by URL in image.url and downloaded by Shopify, not uploaded to Zernio.

Analytics

Shopify exposes no analytics through Zernio.

Inbox

Shopify has no inbox.

What you cannot do

Shopify's connection does not expose:

  • Social posts (POST /v1/posts rejects a Shopify account)
  • Analytics
  • An inbox
  • Media uploads
  • Orders and customers
  • Restoring a deleted blog or article (deletes are permanent, and deleting a blog deletes every article inside it)

Common errors

ErrorCauseFix
400 INVALID_REDIRECT_URLredirect_url is a relative pathPass an absolute http(s) URL or an app scheme.
400 on GET /v1/connect/shopifyshop is not a myshopify.com store domainPass your-store.myshopify.com or the bare your-store prefix.
400 on a Blogs endpointblogId is not numeric, or the account is on a platform without blogsRead the id from List blogs; use a Shopify account.
409 reconnect_requiredThe store was connected before pixel access existed (or with a custom-app token) and a pixel install was calledSend the merchant to details.authUrl; the account keeps its id.
409 invalid_resource_stateUninstalling a tag while the store fires a different tag of that platformRead the install status; uninstall the tag in installedTagId.
403 insufficient_permissionsShopify rejected the request, or the store was connected before product access existed and a Products endpoint was calledReconnect the store: the consent screen grants the missing scopes and the account keeps its id.
404 blog_article_not_foundThe article was deleted, or the id belongs to another blogDeletes are permanent; list the blog's articles to find a current id.
404 product_not_foundThe product was deleted or the id belongs to another storeList the store's products to find a current id.
405The platform lacks this specific Blogs operationNot available for this account.
429Rate limited by Zernio or by ShopifyRetry later. See rate limits.

Deleting an article returns 204, and a later read of it returns 404:

{
  "error": "Article not found",
  "type": "not_found",
  "code": "blog_article_not_found"
}

Zernio stores nothing to restore it from. Error handling covers the envelope.

Related

  • Connecting accounts: the OAuth and Admin-token paths.
  • Blogs API: every blog and article endpoint.
  • Commerce API: products, stock, collections, discounts and Meta catalog sync.
  • Pixels and tracking tags: the tags you can install, per ad platform.
  • Get Shopify OAuth connect URL: every parameter of the connect call.
  • Connect with an Admin token: the token-paste alternative.
  • Platforms overview: the 16 posting platforms.
Was this page helpful?

OpenAI Ads

Run ChatGPT ads on an openaiads account, from connecting an API key to creating chat card campaigns, pixels and server-side conversions.

WordPress

Connect WordPress.com or a self-hosted WordPress site, then create, schedule, update and delete its articles through the Blogs API, manage a WooCommerce store through the Commerce API and install ad platform tracking tags.

On this page

Quick referenceBefore you startConnectCustom-app Admin tokenOAuth scopesWhen the merchant uninstallsPublishList blogsCreate an articleDraftScheduled articleFeatured image and SEOEvery operationCommerceProductsList productsUpdate a productEvery product operationTracking pixelsHow the install worksEvent mappingDeduplicationHow events reach each platformPlatform fieldsMedia requirementsAnalyticsInboxWhat you cannot doCommon errorsRelated