> This page is for Documentation.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fiftytwo-demo.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fiftytwo-demo.ferndocs.com/_mcp/server.

# Sync a product catalog

This is the most common 52ViKING integration: your ERP or PIM owns the product
data, and 52ViKING needs it so tills can sell it.

[Master Data](/api/master-data/overview) is the API for this. It has 101 operations across 18
entity types, but they all follow the same three shapes, so learning one entity
teaches you the rest.

## The shape of every entity

Every entity behaves the same way. Articles, article groups, campaigns, discount
groups, coupons, customers, and operators all give you:

```
GET    /{entity}/v1/chain/{chain}/{id}                    read (chain scope)
GET    /{entity}/v1/chain/{chain}/store/{store}/{id}      read (store scope)
POST   /{entity}/v1/chain/{chain}                         write to all stores
POST   /{entity}/v1/chain/{chain}/store/{store}           write to one store
DELETE /{entity}/v1/chain/{chain}/{id}                    remove from all stores
DELETE /{entity}/v1/chain/{chain}/store/{store}/{id}      remove from one store
```

## Chain scope or store scope?

This is the first real decision, and it is the one people get wrong.

#### Chain scope

`POST /…/chain/{chain}`

The enterprise controller distributes the data to every store. Master data
ends up identical everywhere (article data excepted). This is what you want
for a central catalogue.

#### Store scope

`POST /…/chain/{chain}/store/{store}`

The data is store-specific and handled by that store directly. Use it for
genuine local variation, such as local pricing or a store-only promotion.

> **Warning**
>
> If you are unsure which set to use, ask your FiftyTwo consultant before you
> load production data. Writing a catalogue at the wrong scope is tedious to
> unwind, because you have to know which stores received what.

## Writes are asynchronous

Every `POST` returns `202 Accepted` with a reference in the `Location` header.
Schema validation happens synchronously, so a malformed body fails fast with
`400`. But a `202` only means *accepted*, not *applied*.

#### POST a batch

```bash
curl -i -X POST \
  "https://api.fiftytwo.com/masterdata/articles/v1/chain/1" \
  -H "Authorization: Bearer $FT_TOKEN" \
  -H "Content-Type: application/json" \
  -d @articles-batch.json
```

```http
HTTP/1.1 202 Accepted
Location: /articles/v1/chain/1/status/9f1c…
```

#### Poll the location for a terminal status

```bash
curl "https://api.fiftytwo.com/masterdata/articles/v1/chain/1/status/9f1c…" \
  -H "Authorization: Bearer $FT_TOKEN"
```

Back off between polls, and bound the number of attempts.

## Batch to the limit, not below it

> **Note**
>
> **Use big batches.** The rate limit is roughly 3 requests per second measured
> over a 10-second window, and the body limit is 2 MB. FiftyTwo's own guidance
> is explicit: fill the 2 MB rather than sending many small updates.

A batch of 5,000 articles in one request is both faster and safer than 5,000
requests. Bodies over 2 MB return `413 Payload Too Large`.

```python
import itertools, json

MAX_BYTES = 2 * 1024 * 1024
SAFETY = 64 * 1024          # leave room for the envelope

def batches(articles):
    """Yield the largest batches that stay under the 2 MB body limit."""
    batch, size = [], 0
    for article in articles:
        encoded = len(json.dumps(article).encode()) + 1
        if batch and size + encoded > MAX_BYTES - SAFETY:
            yield batch
            batch, size = [], 0
        batch.append(article)
        size += encoded
    if batch:
        yield batch
```

## Order matters

Load referenced entities before the things that reference them, or the
references dangle:

#### Departments, article groups, discount families

The classification articles will point at.

#### Articles

Now their group and family references resolve.

#### Discount groups and campaigns

These reference articles, groups, families, and labels, so they come after.

#### View groups

PLU button layouts reference articles, so these come last.

## Deduplicate before you send

> **Warning**
>
> Duplicate `object_id` values inside a single POST body are resolved by rules
> described in the [Master Data overview](/api/master-data/overview). Do not rely on
> them. Deduplicate in your own code, where you can see which record won.

## Verify against a till

Once the batch reports success, price a basket containing the new articles with
[Basket API](/api/basket/overview) and check the result matches what you intended. That
exercises the whole path in one call: distribution, article data, and promotion
evaluation.

## Related help-center topics

#### [Articles](/help/store-management/articles)

Every field, as store staff see it.

#### [Article groups](/help/store-management/articles-groups)

The classification hierarchy.

#### [Campaigns](/help/store-management/campaigns)

How promotions are scheduled.