The product catalogue: filling it and keeping it true
A scheduled feed, a file upload, and products learned from orders. Three feed formats, the price unit, and the id that produces no recommendations at all if it does not match your events.
The catalogue is your product list as Segmentic sees it: an id, a name, a price, an image, a link and whether it is in stock. While that list is empty the product recommendation block in an email renders nothing at all, with no error anywhere, just a gap. This page is how to fill it and which decisions quietly break it.
What the catalogue is for
Three things, and none of them works without it:
- The product block in an email, which draws four products in a two-by-two grid.
- The abandoned cart journey, which has to know what was in the basket before it can show it.
- The "similar" and "people also bought" recommendations, which stand on the affinity matrix, and that matrix only knows products the catalogue holds.
That last point matters more than it looks. Behaviour comes from your events, but any id that is not in the catalogue is dropped from the recommendation list. So an account with millions of product_viewed events and an empty catalogue produces zero recommendations.
The id that has to match
This is the most important sentence on the page.
The product id in the catalogue has to be byte for byte what your events send as product_id.
If the site sends SKU-1024 and the feed carries 1024, both halves look correct: the catalogue is full, the events arrive, nothing is wrong in any log, and no recommendation is ever produced. It is the only mistake on this page with no symptom at all.
Before you fill the catalogue, open the Data screen and look at what your events actually send. Then build the feed to match it.
Three ways to fill it
| Way | Who it is for | How often |
|---|---|---|
| A scheduled feed | A shop with a feed URL, or that can produce one | Every six hours, automatically |
| A file upload in the panel | A small or stable catalogue, or a quick start | By hand |
| Your shop platform's webhooks | Any account with Shopify, WooCommerce or Digikala connected | With every order |
They do not conflict and you can have all three. Which one wins is in from your shop platform.
The scheduled feed
In the panel: Catalogue, then Product feed. Give it the URL and the format, and turn it on.
From then on it is read every six hours. The result of the last run sits at the top of that screen: how many products it read, when, and if it failed, why. If nothing has succeeded for more than two days the catalogue page raises an alarm, because a feed that stopped quietly is what puts last season's prices in the next campaign.
Three limits worth knowing: each run has two minutes, reads at most 64 megabytes, and takes at most two hundred thousand products from one document.
The feed URL has to be a public one. An address that resolves to your internal network, to localhost or into a private range is refused, and so is a redirect into one. That is deliberate rather than a shortcoming: a server that fetches an address you choose is, without that check, an attack tool pointed at our own network.
The format is never sniffed. Whatever you pick in the form is what gets read, because a guess that comes out wrong once writes a catalogue of garbage over a working one, and it comes out wrong exactly when your platform changes something and nobody is watching.
The CSV format
The first row has to be column names. These are recognised and anything else is ignored:
| Field | Accepted names | Required |
|---|---|---|
| Id | sku, id, product_id, code | Yes |
| Name | title, name, product_name | Yes |
| Price | price, price_rial, amount | To be shown at all, yes |
| Price before discount | compare_at_price, old_price, list_price | No |
| Category | category, product_type, categories | No |
| Brand | brand, manufacturer, vendor | No |
| Image | image, image_url, image_link | No |
| Product page | url, link, product_url, permalink | No |
| Stock | in_stock, stock, availability, quantity | No |
| Description | description, desc | No |
One malformed row does not cost you the file: it is counted and the rest import. If the id column or the name column is missing entirely the whole file is refused, because a file without those two is the wrong file rather than an empty shop.
The JSON format
Either an array, or an object with a products key:
{
"products": [
{
"sku": "SHOE-1024",
"title": "Runner sports shoe",
"price_rial": 24000000,
"compare_at_rial": 30000000,
"category": "Shoes",
"brand": "Nike",
"image_url": "https://shop.example.ir/img/1024.jpg",
"url": "https://shop.example.ir/p/1024",
"in_stock": true
}
]
}
name for title, id or product_id for sku, image for image_url and link for url are all accepted too.
Leave in_stock out and the product counts as in stock. That is deliberate: a shop exporting only what it sells is the common case, and reading the absence of the field as unavailable would import a catalogue in which nothing can ever be recommended.
The XML format, meaning Google Merchant
If your shop publishes a feed for the price comparison sites, it is probably already this one and you have nothing new to build.
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<item>
<g:id>SHOE-1024</g:id>
<g:title>Runner sports shoe</g:title>
<g:price>24000000 IRR</g:price>
<g:sale_price>19000000 IRR</g:sale_price>
<g:availability>in stock</g:availability>
<g:image_link>https://shop.example.ir/img/1024.jpg</g:image_link>
<g:link>https://shop.example.ir/p/1024</g:link>
<g:brand>Nike</g:brand>
<g:product_type>Shoes</g:product_type>
</item>
</channel>
</rss>
When sale_price is present and lower than price, it is the price and price becomes the struck-through "was" line. availability takes the specification's own values, and out of stock is the only one that makes a product unavailable.
XML is the one format that carries its own currency, which makes it the lowest-risk of the three.
Prices, and their unit
Prices are stored in rial and rendered in messages in toman.
This is the one place where a silent mistake puts a wrong number in front of your customers, so the rule is strict:
| Format | How it is read |
|---|---|
| CSV and JSON | The number is assumed to be rial. If your file is in toman, multiply by ten in the file. |
XML with IRR | Rial |
XML with IRT or TOMAN | Multiplied by ten |
| XML with any other currency | Stored with no price |
That last row is on purpose. A product with no price is visible in the panel and absent from every message, which is a gap somebody finds. A price ten times wrong is a number nobody questions and somebody orders against.
Separators do not matter: 24000000, 24,000,000 and 24.000.000 all read as the same number.
A product priced at zero is never recommended. If the catalogue is full and the recommendations are empty, look at the price column first.
Stock, and a product that leaves the feed
A product that is out of stock never appears in a message. Recommending something that is not for sale is, at the scale of a campaign, a click into a dead end multiplied by the whole send.
A product that is absent from the next run is marked unavailable, not deleted. Deleting it would lose the affinity history it appears in and it would come back with tomorrow's first order anyway. Marking it keeps the row in the panel, which is where somebody notices that half a catalogue went quiet.
There is also a guard worth knowing about. If one run reads less than half of what the last successful run read, the products it did read are written and nothing is marked unavailable. A truncated file, a login page where the feed should be, or an export that stopped half way must not be able to empty a working catalogue at three in the morning.
Uploading a file in the panel
On the Product feed screen, below the feed settings. Give it a CSV, correct the columns it guessed, and see how many products were built and what price the first one got before anything is sent.
Two things this screen has that the feed does not: you choose the price unit right there (toman or rial), and a duplicate id inside one file is reported with its row number.
A large file is chunked for you and you watch the progress. If it fails part way it stops and tells you how many landed, because an import somebody believes is complete is worse than one that visibly failed.
Previewing a recommendation for one user
At the bottom of the Catalogue screen, the preview is always available. After two characters, the same query searches user ids, mobile numbers and email addresses together. For example, 09 finds phones containing it as well as ids or emails containing 09. Matching users appear immediately below the search field. Select one person, leave the recommendation priority on automatic or choose a specific priority, then inspect the result before sending.
If a phrase matches several users, the panel shows each result as a card. Selecting one removes the other cards and keeps the chosen person visible. That choice matters because recommendations come from that user's own behaviour history, and silently taking the first match could preview somebody else's result.
From your shop platform
If you have connected Shopify, WooCommerce or Digikala, every order that arrives already carries the ids, names and prices of what was bought. Those go into the catalogue on their own, with nothing to configure.
With one rule that matters: a webhook only fills a gap and never overwrites anything. An order line knows the least about a product of any source: no image, no category, no compare-at price. So if a feed or a file wrote that product already, the order leaves it alone. The other way round, a shop with a correct feed would watch its catalogue get slightly worse with every order placed.
The currency is read from the platform's own field. If it names one we do not recognise, the product is stored with no price, exactly as with a feed.
Cancellations and returns teach nothing. A returned item is still a real product, but a payload that exists to undo something is a poor first sighting of one, and the next real order carries the same lines.
Syncing from your backend
For an automated sync, publish the catalogue as a JSON feed at an HTTPS URL and add that URL in the panel. Segmentic fetches the feed on schedule and shows the latest run status on the same screen.
Quick troubleshooting
| Symptom | Most likely cause |
|---|---|
| Catalogue full, recommendations empty | The ids do not match the events' product_id. See the id |
| Some products are never recommended | Their price is zero, or they are out of stock |
| Every price is ten times too high | A toman file read as rial. See prices |
| The feed saves but I see no runs | It is not enabled, or its URL is private and was refused. The error is at the top of that screen |
| The feed health card says "Needs attention" | Nothing has succeeded for more than two days. The reason is in last_error on the Feed screen |
| Products went unavailable yesterday | They left the feed. If that was not intended, look at the feed |
What to read next
- Placing events if you do not send
product_viewedandorder_completedyet. Without them a full catalogue still has no history to recommend from. - The event dictionary for an online shop's event list.
- Journeys for building the abandoned cart.