# Shopify

Have a Shopify store? Publish your blog into its own **Blog posts** section.
Every post you publish here becomes a real Shopify article, in your theme, at
your store's URLs, with its SEO title and description, its featured image and
its styling. Articles you wrote yourself are never touched.

Shopify delivery pulls rather than pushes. The DailySmith app polls the same
[sync feed](/docs/delivery-api) the WordPress plugin uses, with your site's
public key, so no Shopify credential ever leaves your store's account and no
DailySmith credential is stored anywhere.

:::callout{severity=info title="Open source, and yours to run"}
DailySmith for Shopify is open source (MIT):
[TechnicalInsideTI/dailysmith-shopify-app](https://github.com/TechnicalInsideTI/dailysmith-shopify-app).
Install it as an app from the link we give you, or run it yourself for a
single store with a token from your own admin (see **Run it yourself**
below). Both do the same sync.
:::

## Install the app

::::steps

:::step[Install DailySmith in your store]
Open the install link and approve it. Shopify shows the two permissions the
app asks for: read and write **content**, which is blogs and articles and
nothing else. No orders, no customers, no products.
:::

:::step[Paste your site key and pick a blog]
The app opens inside your Shopify admin. Enter your site's public key
(`pk_…`, from the site page in the DailySmith dashboard) and choose the
Shopify blog to publish into. A store with one blog has it selected already.
Decide whether articles arrive **published** or **hidden**, so you can look
them over in the admin first, and save.
:::

:::step[Test, then sync]
**Test connection** lists the DailySmith blogs that will sync. **Sync now**
runs the first sync straight away. After that the app syncs on its own,
every hour.
:::

::::

## What arrives

| Shopify field | From |
|---|---|
| Title | The post title |
| Handle (the URL) | The post slug. If an article of yours already holds it, ours gets a numbered suffix and yours keeps the address |
| Content | The rendered HTML, wrapped and styled (below) |
| Excerpt | The generated excerpt |
| Author | The name you set in the app, which starts as your store's name. Set once, when the article is created |
| Published date | The publish time recorded here |
| Visibility | Published or hidden, per your setting. An update re-applies it |
| Featured image | The post's hero, with its alt text, fetched once per image |
| Search engine listing | The SEO title and meta description, in the same fields you edit by hand |

Every DailySmith blog on the site lands in the one Shopify blog you picked.
A blog that publishes in several languages sends only its own language; see
**Languages** below.

## What is never touched

Every article the app creates carries the DailySmith post id in a metafield
of its own (`dailysmith.post_id`). Updates and takedowns go only to the
article carrying the id being synced, never to one found by handle or title.
That is what lets the app share a blog with articles you write yourself:

- **An article of yours on the same handle** keeps its handle and its
  content. Ours takes `handle-2`.
- **An article of ours you delete in the admin** stays deleted until the
  post changes in DailySmith, and is then created again.
- **An article of ours you edit in the admin** is overwritten the next time
  the post changes in DailySmith. Edit the post here instead, or hide it.
- **Uninstalling the app** leaves every article in place. So does
  reinstalling: the app recognises its own articles by the metafield and
  picks them up again rather than creating a second copy.

:::callout{severity=success title="Taking a post down"}
**Take down** on a post stops it being served by us and, within a day, sets
its Shopify article to **hidden**. Never deleted: the article keeps its
address and its content in your admin, and **Publish again** here publishes
the same article again.
:::

## Styling

Posts with rich components (callouts, step guides, comparisons, charts) load
the component stylesheet from DailySmith through a `<link>` at the top of the
article body, and carry your [blog palette](/docs/theming) in one `<style>`
next to it. Plain posts carry only the palette. Everything is scoped to a
wrapper around the article, so it cannot restyle the rest of your theme, and
the wrapper hands typography back to the theme so the article reads like the
rest of your store.

## Languages

:::callout{severity=warning title="One language per Shopify blog"}
A Shopify blog is single-language, so the app mirrors only a blog's **own
language**. A DailySmith blog that also publishes in additional languages
keeps serving those from your own domain. If you want two languages in
Shopify, run two DailySmith blogs, one per language, and point each at its
own Shopify blog through a second install of the app.
:::

## Run it yourself

You do not need us to host the app. For a single store, create a custom app
in your own admin and run the service anywhere that keeps one process
running:

::::steps

:::step[Create a custom app in Shopify]
In your Shopify admin, go to **Settings → Apps and sales channels → Develop
apps** and create an app. Under **Admin API integration**, tick
`read_content` and `write_content`, install it, and copy the **Admin API
access token** (`shpat_…`). Shopify shows it once.
:::

:::step[Run the service]
```bash
git clone https://github.com/TechnicalInsideTI/dailysmith-shopify-app
cd dailysmith-shopify-app
npm ci
cat > .env <<'ENV'
SHOPIFY_CUSTOM_APP_TOKEN=shpat_…
SHOP=my-store.myshopify.com
DAILYSMITH_PUBLIC_KEY=pk_…
SHOPIFY_BLOG_HANDLE=news
ENV
node --env-file=.env bin/sync.js
```

That runs one sync and prints what it did. `node --env-file=.env server.js`
keeps running and syncs every hour. The repository's README covers the
container image, the other settings, and running it as a multi-store app.
:::

::::

## Troubleshooting

| What you see | What it means |
|---|---|
| `unknown or unverified site key` | The `pk_…` key is wrong, or the site is not verified yet. Copy it again from the site page; verify the site first. |
| "This store has no blog yet" | Create one under **Online Store → Blog posts → Manage blogs**, then reload the app. |
| "Not configured" | Save the site key and pick a blog; the sync only runs once both are set. |
| "Shopify refused the access token" | The app was uninstalled, or (custom app) its token was revoked. Reinstall, or create a new token and restart the service. |
| An article arrived without its image | Shopify fetches the image from DailySmith when the article is created. If that fetch failed, the next change to the post sends it again. |
| Two articles for one post | Someone created the second one by hand, or an older copy was written before the app was installed. Delete the one without the `dailysmith.post_id` metafield; the app never creates two. |

## Security notes

- The app stores your site's **public** key, the same key your embed
  snippet carries. It grants read access to already-published content and
  nothing else.
- The Shopify permission is **content** only. The app can create and update
  blog articles; it cannot see orders, customers, products or settings.
- The app stores no customer data. Uninstalling it deletes everything it
  kept about your store: its settings, its access token, and its list of
  which article mirrors which post. The articles stay.
- A custom app token gives write access to your store's content. Keep it in
  the service's environment, never in a page, and revoke it from the custom
  app's page in your admin the day you stop using it.
