Shopify
Connect a Shopify store to Zernio and manage its blogs and articles through the API - create, schedule, update and delete storefront blog posts
Shopify is a connect-only platform. A connected store does not publish social posts and reports no analytics; it powers the Blogs API, which manages the blogs and articles on the store's Online Store section.
Quick Reference
| Property | Value |
|---|---|
| Platform value | shopify |
| What it manages | Storefront blogs and blog articles |
| Auth | OAuth 2.0 (store domain required) or a custom-app Admin token |
| Scopes | read_content, write_content |
| Social posting | No |
| Analytics | No |
| Media requirements | None (article images are referenced by URL) |
| Scheduling | Yes, native (Shopify publishes at publishDate; no Zernio queue) |
| Drafts | Yes (isPublished: false) |
| Article body | HTML (bodyHtml) |
| SEO fields | Yes (seo.title, seo.description) |
| Pagination | Cursor (limit 1-50, default 20, plus nextCursor) |
Before You Start
Shopify does not behave like the social platforms. A store connects as an account on a profile, but it never appears as a publishing target for posts. Everything you do with it goes through the Blogs endpoints.
- You need the store's
myshopify.comdomain to start OAuth. Shopify has no store picker and no lookup that maps a merchant to their shops, so an authorization URL can only be built for a domain you already know. Collect it from the merchant before calling the connect endpoint. - A merchant arriving from the Shopify App Store never types it. Shopify supplies the domain to Zernio itself on that path, so an install started from the listing skips the question entirely.
- Blog and article ids are Shopify's own numeric ids, not Zernio object ids. Read them from the API responses; never construct them.
Connecting a Store
Two paths, both landing on the same connected account.
OAuth (recommended)
GET /v1/connect/shopify returns an authUrl rather than redirecting, so you can drive the flow from your own product. Send the merchant to that URL; 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.
const { data } = await zernio.connect.getShopifyConnectUrl({
query: {
profileId: 'YOUR_PROFILE_ID',
shop: 'your-store.myshopify.com',
redirect_url: 'https://yourapp.com/connected',
},
});
// Redirect the merchant to `data.authUrl` to start the OAuth flow.res = client.connect.get_shopify_connect_url(
profile_id="YOUR_PROFILE_ID",
shop="your-store.myshopify.com",
redirect_url="https://yourapp.com/connected",
)
# Redirect the merchant to res["authUrl"].curl "https://zernio.com/api/v1/connect/shopify?profileId=YOUR_PROFILE_ID&shop=your-store.myshopify.com" \
-H "Authorization: Bearer YOUR_API_KEY"redirect_url accepts an http(s) URL, a relative path, or a custom app scheme such as myapp://callback for mobile deeplinks. The bare your-store prefix is accepted in place of the full domain. Reconnecting the same profile to the same store refreshes the stored token in place rather than creating a second account.
Custom-app Admin token
If you would rather not run a browser flow, the merchant can create a custom app in their own Shopify admin (Settings → Apps and sales channels → Develop apps) with the read_content and write_content scopes, then hand you its Admin API access token:
curl -X POST "https://zernio.com/api/v1/connect/shopify/token" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "YOUR_PROFILE_ID",
"shop": "your-store.myshopify.com",
"accessToken": "shpat_..."
}'The store domain is required here too: an Admin token carries no indication of which store it belongs to.
OAuth Scopes
| Scope | What it enables |
|---|---|
read_content | Read the store's blogs and articles |
write_content | Create, update and delete blogs and articles |
Content scopes only. Zernio requests no access to customers, orders, products or payments.
Working With Blogs
A store has one or more blogs (Shopify creates a "News" blog by default), and each blog holds articles. Start by listing the blogs to get the id you will write into:
curl "https://zernio.com/api/v1/accounts/ACCOUNT_ID/blogs" \
-H "Authorization: Bearer YOUR_API_KEY"{
"platform": "shopify",
"blogs": [
{ "id": "121793282419", "platform": "shopify", "title": "News", "handle": "news" }
],
"nextCursor": null
}| Operation | Endpoint |
|---|---|
| List blogs | GET /v1/accounts/{accountId}/blogs |
| Create a blog | POST /v1/accounts/{accountId}/blogs |
| Get a blog | GET /v1/accounts/{accountId}/blogs/{blogId} |
| Update a blog | PATCH /v1/accounts/{accountId}/blogs/{blogId} |
| Delete a blog | DELETE /v1/accounts/{accountId}/blogs/{blogId} |
| List articles | GET /v1/accounts/{accountId}/blogs/{blogId}/articles |
| Create an article | POST /v1/accounts/{accountId}/blogs/{blogId}/articles |
| Get an article | GET /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
| Update an article | PATCH /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
| Delete an article | DELETE /v1/accounts/{accountId}/blogs/{blogId}/articles/{articleId} |
Publishing an Article
curl -X POST "https://zernio.com/api/v1/accounts/ACCOUNT_ID/blogs/BLOG_ID/articles" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"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
}'Publishing behavior:
isPublished: truepublishes immediately.isPublished: falsekeeps the article as a draft.- A future
publishDateschedules the article natively on Shopify. Shopify publishes it at that time; no Zernio queue is involved, and the article reads back asisPublished: falsewithpublishedAtset to the future date until then. seo.titleandseo.descriptionmap to Shopify'stitle_taganddescription_tagmetafields, which is what themes read for the page title and meta description.
Behavior Worth Knowing
The handle does not follow the title. Shopify slugs the handle once, at creation. Renaming an article later leaves the original URL in place, which is what you want for a published post but surprises you when the title and URL no longer match. Set handle explicitly if the URL matters.
- Tags come back alphabetized. Shopify sorts them, so the order you sent is not the order you read.
- Deletes are permanent. An article delete returns
204and subsequent reads return404 blog_article_not_found. Deleting a blog deletes every article inside it. Zernio stores nothing to restore either from. blogIdmust be numeric. A non-numeric value is rejected with400before it reaches Shopify.- Accounts on other platforms return
400from the Blogs endpoints; a blogs-capable platform that lacks one specific operation returns405.
Not Supported
Shopify connects for content only. There is no social posting, no analytics, no inbox, and no media pipeline: article images are referenced by URL through the image field rather than uploaded to Zernio. Product, order and customer data are out of scope, and the granted scopes do not permit them.