## Overview

**Sync Studio** connects external e-commerce platforms and marketplaces to your **native catalog** in
ConversaLabs. You set up **sync sources** that can:

- **Import (inbound)** — pull the products from the external platform into the native catalog;
- **Publish (outbound)** — push products from the native catalog to the external platform;
- **Keep both ways** — combine import and publish on the same source.

Sync Studio is different from two nearby features:

- **WhatsApp Business Catalog (Meta) sync** links the native catalog to the Meta catalog so you can send
  products on WhatsApp — it has its own article.
- The **e-commerce lifecycle** handles **sale events** (abandoned cart, pending PIX, approved purchase,
  refund) that turn into cards in the conversation — it also has its own article.

Sync Studio is about the **product catalog**: what is for sale, with name, price, and images.

## Prerequisites

- The **Catalog & Commerce** module enabled for your account.
- **Administrator** permission to manage sync sources.
- An account and **access credentials** on the external platform (token, app keys, or authorization,
  depending on the connector).
- Recommended: a native catalog already organized into categories before publishing products outward.

## Step by step

1. In the **Catalog** area, open **Sync Studio** (sync sources) and click to create a **new source**.
2. Pick the **connector/preset**: Generic source, Shopify, WooCommerce, Magento, PrestaShop, Medusa,
   Mercado Livre, Nuvemshop, Hotmart, Kiwify, or OLX.
3. Set the **direction**: **Inbound** (import), **Outbound** (publish), or **Both ways**.
4. **Authenticate** the source according to the connector:
   - **Mercado Livre** — click **Authorize**; you are taken to the platform's login screen and, once you
     approve, returned to the fixed address `/catalog_oauth/callback`. Tokens are stored encrypted.
   - **Hotmart / Kiwify** — provide `client_id` and `client_secret` (plus `account_id` for Kiwify).
     Hotmart's `basic_token` remains accepted for legacy sources but is derived automatically when it
     is absent. Kiwify receives the credentials as a form and generates the token.
   - **Generic source, Shopify, WooCommerce, Magento, PrestaShop, Medusa** — choose Bearer, header,
     **Basic** (username and password), or **URL query parameter** authentication, then set the mapping.
   - **Nuvemshop** — paste the store token and the `store_id`.
5. Use **Test connection** for a saved Hotmart or Kiwify source. For a Generic source, use the **live
   preview** before saving: it makes the same call as sync and shows the response, actual parsed format,
   detected fields, mapping suggestions, and the first mapped record.
6. Save and click **Sync now** to run the first import (or **Publish now** for an outbound source).
7. Follow the paginated and filterable **run history** to see every sync, with times in the account
   timezone, direction, and result (items read, created, updated, skipped, or failed). The source list
   also shows safe counters and the latest error without exposing credentials. You can export the
   filtered history or explicitly selected runs as CSV; a partial export keeps failed run IDs visible.

## Settings & options

- **Connector / preset (source_type)** — the origin/destination platform.
- **Direction** — inbound, outbound, or both ways.
- **Credentials** — stored **encrypted**, never shown back, and never written to logs.
- **Sync interval** — how often the source is synced automatically (on top of manual, on-demand syncs).
- **Source state** — a disabled source remains editable, but it does not accept webhooks or execute
  **Sync now**, **Publish now**, or queued pull, publishing, and Meta-validation work. Re-enable it only
  after reviewing its direction, capabilities, and credentials.
- **Pagination** — large catalogs are walked page by page automatically (Shopify follows the `Link`
  header, WooCommerce advances by `page`); Generic JSON sources can configure the pagination style. If
  a run reaches the 200-page safety limit while another page still exists, it finishes as
  **partial/truncated** and preserves the cursor; the next run resumes from that point. The cursor is
  cleared only after the feed actually reaches its end.
- **Format and products root** — Generic sources detect JSON, NDJSON, XML, CSV, or TSV from the response;
  you can choose a format manually. XML uses an **XPath** root (for example, `//products/product`); the
  other formats use a dot path.
- **Suggested mapping** — the preview suggests, but never saves by itself, Portuguese, English, and
  Spanish field matches with confidence. Review before applying. In addition to name and ID, price,
  compare-at price, currency, SKU, stock, brand, and external URL are mappable; `items.0.price` selects
  the first array position.
- **Source webhook** — each source has its own webhook address
  (`/webhooks/catalog/:account_id/:source_id`). Where
  the platform allows it, ConversaLabs registers it with one click via **Register webhook** (Nuvemshop,
  Kiwify); on others, you paste the address into the platform's own dashboard (Hotmart).
- **Inbound sync secret** — used by bulk-sync API clients and masked in the source list. **Rotate inbound
  secret** invalidates the previous value immediately, records the operation when the native audit
  trail is enabled, and reveals the new value once; update every sender.
- **Platform webhook secret** — a value provided by the platform to verify commerce events. It is
  write-only and never returned. To replace it, paste the new value while editing the source and update
  the external dashboard too; ConversaLabs does not invent or rotate that value on the platform.
- **Bidirectional Generic source** — configure the event map and field paths in the screen itself.
  Inbound requests require `X-Webhook-Signature: t=…,v1=…`, reject a source without a secret, enforce a
  replay window, and deduplicate the delivery ID. An outbound-only source refuses inbound deliveries.
  For outbound, provide an HTTP(S) destination without credentials, query parameters, or fragments;
  authentication uses the shared HMAC secret. Events have searchable history, automatic retries, and
  confirmed single or bulk manual replay. A delivery that
  finds the source disabled, deleted, changed, or without its secret fails before any external request
  and records the reason in that history.
- **Generic webhook product, variation, and items** — product and variation paths start at the event
  root. Set the items array path first, then map fields relative to each item. Blank fields are never
  inferred. Once mapped, values are snapshotted on the CRM order and sent only to account webhooks that
  opted in to **commerce details**.
- **Duplicate and loop protection** — every imported item keeps the origin identifier (`external_id` /
  `retailer_id`) and the source. On later syncs this updates the same product instead of recreating it,
  and prevents echoing a change back to the source it came from.

### Import historical Hotmart or Kiwify sales

For a saved Hotmart or Kiwify source, open **Management → Backfill sales**. Choose both dates within
the provider window: up to 31 calendar days for Hotmart or 90 days for Kiwify. You can filter by product
and by the provider's documented status/payment values; Kiwify also supports an affiliate filter. With
no Hotmart status filter, its API returns only `APPROVED` and `COMPLETE`; choose another documented status
explicitly when you need it. Click **Preview sales**: this call
is read-only, uses each provider's official pagination, and does not write a contact, organization,
event, or order. Review the local action for each row, then explicitly select at least one and at most
500 IDs before queuing the import. Omitting the selection never means “import everything.”

The import matches only existing account contacts through exact fields from that sales-list endpoint.
Hotmart Sales History provides buyer email (name/ucode are not identity matches); Kiwify `GET /sales`
provides email, mobile and CPF. An organization is associated only through the exactly matched contact's
current primary organization. It never guesses between conflicting matches or invents a CNPJ field that
the Kiwify list does not document. A buyer without an existing contact, or a provider state without a
faithful local meaning, is shown as non-importable; history
import never creates people or companies. Imported lifecycle events and CRM Orders preserve the
provider's order/approval timestamps and are marked historical. This suppresses customer messages,
automation fan-out, commission and ranking credit, engagement, CAPI, stock effects, and outbound account
webhooks while still populating the native order registry.

Use **Run history** to inspect selected, created, updated, repaired, skipped and failed counts, the exact
failed/skipped transaction IDs, and whether the global provider scan was truncated. An exact duplicate
request reuses the active run, and execution is locked per account/source. A completed or partial run
offers **Undo import** only while every locally created order/event is still untouched and unlinked; undo
is atomic, never contacts Hotmart/Kiwify, and never deletes contacts or organizations.

## Use cases

- Import a **Shopify** or **WooCommerce** store into the native catalog and serve customers from there.
- Publish native products to a **marketplace** (when the connector supports outbound).
- Keep **Mercado Livre** or **Nuvemshop** connected to feed the native catalog.
- Import **digital products** from **Hotmart** or **Kiwify** and still receive sale events in the
  conversation.

### Capabilities by platform

Each connector declares what it supports; the interface enables the direction and the buttons according
to that capability.

| Platform | Import (inbound) | Event webhook | Historical sales | Authentication |
|---|---|---|---|---|
| Generic source (JSON, NDJSON, XML, CSV, or TSV) | Yes | Yes (configurable HMAC signature) | No | Bearer, header, Basic, or URL query parameter |
| Shopify | Yes | — | No | Token / header |
| WooCommerce | Yes | — | No | Token / header |
| Magento, PrestaShop, Medusa | Yes | — | No | Token / header |
| Mercado Livre | Yes | — | No | OAuth (Authorize) |
| Nuvemshop | Yes | Yes (self-register) | No | Store token (paste) |
| Hotmart | Yes (products) | Yes (in Hotmart panel) | Yes (governed preview and selection) | Client credentials |
| Kiwify | Yes (products) | Yes (self-register) | Yes (governed preview and selection, up to 90 days) | Client credentials |
| OLX | No (no read API) | — | No | Partner only |

- **Publishing (outbound)** to the **WhatsApp Business Catalog** has its own article; the store/marketplace
  connectors above focus on **import** in this version. **Not every platform is bidirectional.**
- The **event webhooks** above deliver **sale events** (not products) and feed the **e-commerce
  lifecycle** — see that article.
- **OLX** offers no public read API: the integration exists only for approved partners, who publish an
  ad feed (autoupload). For that reason the OLX connector ships disabled in Sync Studio until partner
  access is granted.

## Tips, limits & best practices

- **Credentials stay on the server**: they are encrypted, never returned to the screen, and never appear
  in logs. OAuth connectors (Mercado Livre) **refresh the token on their own** through the granted
  authorization.
- **Respect the source platform's pagination and limits**; very large catalogs are read across several
  pages and may take longer on the first import.
- **The description arrives as plain text**: stores such as Nuvemshop/Tiendanube and Shopify keep
  the product description in HTML. On import the platform **converts it to text**, preserving
  paragraphs, line breaks and lists — the tags never reach you or the customer, since the
  description travels as text in the message sent in the conversation and in the Meta catalog. A
  description you type yourself is stored exactly as you wrote it.
- **The public link is included for Nuvemshop/Tiendanube**: import fills the **Public product link**
  field with the canonical URL supplied by the store. For responses that contain only the
  page-friendly identifier, the platform combines it with the store domain. The next sync — the
  scheduled one or the one you start with **Run now** — also fills the link for products imported
  previously; nothing needs to be imported again.
- **Without a domain, products still sync**: if the store does not report a public domain, or the
  lookup of the store details fails, the products are imported as usual, **without** the link. The
  run ends as **Partially completed**, with a notice that the store's public domain could not be
  retrieved, instead of failing as a whole.
- **Filling in the link by hand is for products without a link at the source**: on a synced
  Nuvemshop/Tiendanube product the link is built from the store — the address of the product page
  plus the store's domain. A hand-typed link is therefore **replaced on the next sync** whenever the
  store has a public domain. What the sync never does is clear the field: while the source has no
  link for that product, what you typed in **Public product link** stays.
- **Custom attributes are preserved**: the sync updates only the attributes the source sends and keeps
  the ones you filled in on the product.
- **One faulty item does not block the others**: when a product cannot be saved, the run carries on
  with the rest and ends as **Partially completed**. **Run history** shows up to 50 of those products,
  with the identifier at the source, the name and the reason.
- **On Nuvemshop/Tiendanube, two products with the same address stay two products**: the address of
  the product page (the page-friendly identifier) belongs to a single product in the store. When two
  active products use the same one, each keeps its own record in the catalog; the second one gets the
  address followed by its identifier at the source. If a product was deleted and recreated in the
  store with the same address, the existing record is reused and the run says so under **Products to
  review**.
- **That rule applies only to the Nuvemshop/Tiendanube connector**: every other source and the
  inbound API keep grouping into a single product the rows that repeat the same address (`slug`) or
  the same name — that is how a source with one row per variation becomes one product with several
  variations, and how the WhatsApp Business catalog groups same-named items, as it always did.
- **Catalogs with more than 40,000 products on Nuvemshop/Tiendanube**: the store is read in parts, and
  only a complete read of the store can confirm that the old product no longer exists. In those
  catalogs a product recreated with the same address does not take over the old record: it is listed
  among the products that were not synced, with the reason that another product uses the same
  address. Give the new product its own address in the store so that it is imported as a new record.
- **Major units for money**: prices stay in major units (`4.97` = R$4.97). The platform does **not**
  divide by 100.
- Run the **first import outside peak hours** to review the result calmly.

## Troubleshooting

- **Mercado Livre authorization failed**: confirm the platform app uses the fixed return address
  `/catalog_oauth/callback` and redo the **Authorize** flow (the authorization `state` expires within a
  few minutes).
- **Products were not imported**: use **Test connection** to see the real response; check the token/keys,
  the `store_id`/`account_id` where applicable, and whether the chosen connector is the right one. The
  Generic preview shows the platform's reason with secrets removed.
- **The webhook does not fire**: confirm the source was **registered** (Nuvemshop/Kiwify) or that the
  address was pasted into the platform panel (Hotmart), and that the **webhook secret** is filled in.
- **Duplicate items**: duplication is avoided by the origin identifier (`retailer_id` / `external_id`);
  if duplicates appear, check whether the items arrived with identifiers different from what already
  existed.
- **The public product link is empty**: on the source card, check the status and the notice of the
  last run. The notice that the store's public domain could not be retrieved means the products came
  in without a link: check the domain in Nuvemshop/Tiendanube and click **Run now**, or type the link
  on the product itself — it holds until the sync can build the link from the store. If the run shows
  as **Failed**, the error text states the reason (for example, an invalid token) and the read stopped
  at that point: products from the pages already read stay saved and the rest were not updated — if it
  failed on the very first read, none was. Fix the cause and click **Run now**: the next run resumes
  where the previous one stopped.
- **Some products do not update**: open the **Partially completed** run in **Run history** and read
  the list of products that were not synced and the reason for each one. Fix the item at the source,
  or the conflicting product in the catalog, and run the sync again.
- **The run shows "Products to review"**: the list points to products that were synced but whose
  identification at the source needs attention — a product that took over the record of another one
  that no longer exists, or two active products that already shared the same record. A shared record
  is neither split nor extended by the sync: the first product stays its owner and, for the second,
  only the variations already on it are updated (price and stock), so that one does not overwrite the
  other. A new variation of the second product is not imported, and changing its address in the store
  does not separate it: it only gets a record of its own if it arrives as a new product, with
  variations that are not on the shared record. The run is only **Partially completed** when a new
  case appears: the first time a pair is detected, or when a pair that had been fixed comes back.
  Otherwise the notice stays listed and the run ends **Completed** — including when one of the cases
  goes away and others remain. When the same run has more than one notice — for example, this one and
  the one about the store's public domain — both are shown together.

## See also

- [Native catalog: products, categories, images, and prices](/hc/ajuda/articles/catalog-commerce-catalogo-nativo-en)
- [Sync and WhatsApp Business Catalog](/hc/ajuda/articles/catalog-commerce-sync-whatsapp-business-catalog-en)
- [E-commerce lifecycle: Kiwify/Hotmart/Nuvemshop/Shopify webhooks](/hc/ajuda/articles/catalog-commerce-commerce-lifecycle-en)

### Nuvemshop variation names

Synchronization preserves the platform’s size and color options and composes the variation name from those values. Run synchronization again to populate options on previously imported products. Repeating the same synchronization updates the existing variations without duplicating them; an incomplete response does not erase names and options already imported successfully.