Zernio
Zernio
Overview

Social Posting

XInstagramFacebookLinkedInTikTokYouTubePinterestRedditBlueskyThreadsGoogle Business ProfileSnapchat

Messaging

WhatsAppTelegramDiscordSlack

Telephony

Phone NumbersVoice & CallsSMS

Advertising

Meta AdsGoogle AdsLinkedIn AdsTikTok AdsPinterest AdsX AdsOpenAI Ads

Commerce

Shopify
Dashboard
llms.txtOpenAPI
OverviewPlatformsAPI ReferenceResources

Shopify

Connect a Shopify store and create, schedule, update and delete its blog articles through the Blogs API; a store publishes no social posts.


Create, schedule, update and delete the blog articles of a Shopify store with the Blogs API and 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
AuthOAuth 2.0 (store domain required) or a custom-app Admin token
Scopesread_content, write_content
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 and article 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 and write_content 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

Content scopes only. Zernio requests no access to customers, orders, products or payments.

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}

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
  • Products, orders and customers (the granted scopes do not permit them)
  • 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.
403 insufficient_permissionsShopify rejected the requestReconnect the store to restore access.
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.
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.
  • 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.

On this page

Quick referenceBefore you startConnectCustom-app Admin tokenOAuth scopesWhen the merchant uninstallsPublishList blogsCreate an articleDraftScheduled articleFeatured image and SEOEvery operationPlatform fieldsMedia requirementsAnalyticsInboxWhat you cannot doCommon errorsRelated