Storefronts and marketplaces
Write the product once, publish it everywhere
The same jacket is one product to you and four different documents to four channels: a shorter title here, a category tree there, an attribute set that only one of them asks for. The mistake is not the retyping — it is that each channel slowly becomes its own version of the truth, and nobody can say which one is right. One record, rendered per channel, fixes that. The part worth building carefully is the refusal: a channel whose required fields are not filled should not be published half-formed.
- Triggers
- Two
- Runs
- Hourly
- Stack
- 4 tools
- Durable execution
- Not required
The flow
The top track keeps one record clean. The bottom track renders it per channel on a timer, and publishes only the channels it can fill completely.
A product record changes
Sheets · Airtable
Normalise the record
n8n · Make
Write the master row
Sheets · Airtable
Every hour
n8n 排程
Render per channel
n8n
Channel fields complete?
n8n IF
Publish to the channel
Shopify Admin API
Hold it and say what is missing
Slack · WhatsApp
Step by step
- 01
A product record changes
Sheets · AirtableSomebody edits the master row — a price, a photo, a spec line. This is the only place anyone is allowed to edit. Every channel is downstream of it, and no channel edits back.
- 02
Normalise the record
n8n · MakeTrim to a single shape: title, long description, dimensions, materials, images in a fixed order, one category value per channel. Do the shortening and the category mapping here, once, rather than four times inside four branches.
- 03
Write the master row
Sheets · AirtableStore the normalised record next to what each channel currently holds. The gap between those two columns is the whole job — without it you cannot tell an unpublished edit from a channel that quietly rejected one.
- 04
Every hour
n8n 排程The publishing pass runs on a timer rather than on the edit. Editing is bursty — somebody fixes eleven rows in four minutes — and a timer collapses that into one pass per channel instead of eleven.
- 05
Render per channel
n8nBuild one payload per channel from the master row: its own title length, its own category id, its own attribute names. The record does not change between channels. Only the rendering does.
- 06
Channel fields complete?
n8n IFCheck the payload against what that channel requires before you send it. This is where a missing attribute is cheap; after the call it is a rejected listing, or worse, a published one with a blank where the size should be.
- 07
Publish to the channel
Shopify Admin APISend the full record, not a patch. Sending the whole thing is what makes the master row authoritative — but it also means an incomplete payload erases what it omitted, which is why the check sits in front of this step and not after it.
- 08
Hold it and say what is missing
Slack · WhatsAppName the product, the channel, and the exact field. A message that says only "listing failed" sends someone into the seller centre to find out which of forty attributes it was, which is the work you were removing.
What the platform will not allow
Publishing a whole record is not a stylistic choice. It is what the write actually does, and the consequences are documented.
productSet "performs multiple operations to create or update products in a single request", and for list fields — variants, collections, metafields — it "Creates new entries, updates existing ones, and deletes omitted entries". A payload rendered from a partial record does not skip what it left out. It deletes it.
Shopify Admin GraphQL API — productSetThe same mutation runs synchronously by default and returns the updated product; pass synchronous: false and you get a ProductSetOperation instead, whose status you have to poll. Shopify also notes that "By default, stores have a limit of 2048 product variants for each product" — a ceiling worth knowing before a size-by-colour matrix is generated rather than typed.
Shopify Admin GraphQL API — productSetFor a catalogue-sized push, bulk import takes a staged JSONL file capped at 100MB, one input per line. From API version 2026-01 "each app can run up to five bulk mutation operations per shop simultaneously"; on earlier versions it was one at a time per type. Result files are authenticated and expire after one week, so a run nobody read is a run that has to happen again.
Shopify — Bulk import data with GraphQLIf the master record lives in Airtable, the read side is metered too: "The API is limited to 5 requests per second per base", with 50 per second across a token, and exceeding it returns 429 and "you will need to wait 30 seconds before subsequent requests will succeed". An hourly pass that reads row by row hits this long before the storefront notices anything.
Airtable Web API — Rate limits
When this is not worth building
Three cases where the upkeep costs more than the retyping.
You hold a few dozen products that change once a season. Rendering them by hand four times a year is less work than owning a mapping table that has to be corrected every time a channel renames a category.
The channels genuinely want different products. If one carries a bundle and another carries the single unit, they are not the same record rendered differently — forcing them into one row creates a translation layer nobody can read.
Nobody has agreed which system is the master. Until that is settled, the flow does not remove disagreement about the catalogue; it publishes one side of it on a timer, and the other side keeps editing underneath.