Product sync
Connect the store, send the whole catalog, then keep it current with small updates.
All calls go to https://manova.ir/api/v1/external/store with the connection key as a bearer token.
Full request and response schemas are in the API reference.
1. Connect
Tell Manova where the shop is and where to send order lookups. Call it once when the integration is set up, and again whenever any of these values changes.
curl -X POST https://manova.ir/api/v1/external/store/connect \
-H "Authorization: Bearer $MANOVA_STORE_KEY" \
-H "Content-Type: application/json" \
-d '{
"siteUrl": "https://shop.example.com",
"apiUrl": "https://shop.example.com/api/manova",
"currency": "IRT",
"pluginVersion": "my-cms-1.0"
}'apiUrlmust be on the same domain assiteUrl(with or withoutwww.) and reachable from the internet. Manova appends/orderto it for order lookups.- The shop's domain is added to the assistant's allowed domains, so the widget works there right away.
- Connecting a different shop with the same key deletes the products of the previous one.
The response tells you what to embed:
{
"publicKey": "pk_8Wc1...",
"botName": "دستیار فروشگاه",
"active": true,
"widgetUrl": "https://manova.ir/widget.js",
"ordersEnabled": true,
"panelUrl": "https://manova.ir/workspace/site/3c0d.../store",
"products": 0,
"inStock": 0,
"lastSyncAt": null
}GET /store returns the same object at any time, which makes it a good connection check.
2. Full sync
A full sync sends every published product and lets Manova delete the ones you no longer have.
Start
Pick a random syncId (8 to 40 characters: letters, digits, _ and -).
{ "syncId": "sync_20261030_a81f", "phase": "start" }Send batches
Send products in batches of up to 100, each with the same syncId:
{
"syncId": "sync_20261030_a81f",
"products": [
{
"id": 1042,
"name": "کفش رانینگ مردانه مدل آریا",
"url": "https://shop.example.com/product/aria-running",
"image": "https://shop.example.com/media/aria.jpg",
"sku": "ARIA-42",
"type": "variable",
"price": 2450000,
"price_text": "۲٬۴۵۰٬۰۰۰ تومان",
"regular_price_text": "۲٬۹۰۰٬۰۰۰ تومان",
"in_stock": true,
"stock_quantity": 7,
"categories": ["کفش", "ورزشی"],
"tags": ["رانینگ", "سبک"],
"summary": "<p>رویه مش تنفسپذیر، زیره EVA.</p>",
"cart_url": "https://shop.example.com/cart?add=1042",
"total_sales": 318
}
]
}{ "ok": true, "saved": 1, "removed": 0 }Finish
{ "syncId": "sync_20261030_a81f", "phase": "finish" }{ "ok": true, "removed": 12, "products": 1840, "inStock": 1610 }Every product that was not sent in this sync is removed.
Starting a new sync replaces the previous one. A batch or a finish carrying an older syncId gets
409 CONFLICT: stop that sync, because a newer one is running.
3. Incremental updates
When a product changes, send just that product, without syncId. Send the ids of deleted products in deleted:
{
"products": [{ "id": 1042, "name": "کفش رانینگ مردانه مدل آریا", "url": "https://shop.example.com/product/aria-running", "price_text": "۲٬۲۰۰٬۰۰۰ تومان", "in_stock": true }],
"deleted": [977, 978]
}A product with "visible": false is removed too, which is handy for drafts and hidden products.
Run a full sync now and then (for example nightly) to catch anything an update missed.
Product fields
id, name and url are required; a product without a name or a URL on the shop's own domain is skipped.
Each update replaces the stored product, so always send every field you have.
Prop
Type
Links on product cards get utm_source=manova&utm_medium=assistant, so sales from the chat show up in your analytics.
Errors
| Status | Code | Meaning |
|---|---|---|
401 | UNAUTHENTICATED | The key is wrong or was replaced by a new one in the panel. |
409 | CONFLICT | Products were sent before connect, or a newer full sync has started. |
422 | VALIDATION_ERROR | A field is invalid; details names it. Also when apiUrl is not on the shop's domain or is not public. |