Product Sync Integration Guide
This guide covers how to synchronize products, variants, and inventory from your external e-commerce platform (like Shopify, WooCommerce, etc.) to VesuvioPay using the SDK Product API.
Overview​
The VesuvioPay Product Sync API enables you to:
- Sync products and variants in bulk
- Create, update, and delete individual products
- Update inventory levels in real-time
- Link existing VesuvioPay products with external platform IDs
- Manage product options and variants
Authentication​
All Product API endpoints require a Private API Key passed in the X-Api-Key header.
X-Api-Key: your-private-api-key
Product Sync Workflow​
Step 1: Bulk Product Synchronization​
The bulk sync endpoint is the recommended way to synchronize your entire product catalog or perform large updates.
Endpoint: POST /api/v1/sdk/products/sync
Request Example:
{
"products": [
{
"externalId": "shopify_12345",
"title": "Premium T-Shirt",
"description": "High-quality cotton t-shirt",
"vendor": "My Brand",
"productType": "Apparel",
"tags": ["clothing", "tshirt", "cotton"],
"status": "Active",
"images": [
{
"url": "https://example.com/images/tshirt-front.jpg",
"altText": "T-shirt front view",
"position": 1
}
],
"variants": [
{
"externalId": "shopify_variant_001",
"title": "Small / Red",
"sku": "TSHIRT-S-RED",
"barcode": "123456789",
"price": 29.99,
"compareAtPrice": 39.99,
"inventoryQuantity": 100,
"inventoryTracking": true,
"weight": 0.2,
"weightUnit": "kg",
"options": [
{ "name": "Size", "value": "Small" },
{ "name": "Color", "value": "Red" }
]
},
{
"externalId": "shopify_variant_002",
"title": "Medium / Red",
"sku": "TSHIRT-M-RED",
"price": 29.99,
"compareAtPrice": 39.99,
"inventoryQuantity": 150,
"inventoryTracking": true,
"weight": 0.2,
"weightUnit": "kg",
"options": [
{ "name": "Size", "value": "Medium" },
{ "name": "Color", "value": "Red" }
]
}
]
}
],
"deleteNotIncluded": false
}
Response:
{
"success": true,
"message": "Products synchronized successfully",
"data": {
"totalProducts": 1,
"created": 1,
"updated": 0,
"deleted": 0,
"failed": 0,
"results": [
{
"externalId": "shopify_12345",
"productId": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"status": "created",
"variantsCreated": 2,
"variantsUpdated": 0
}
]
}
}
Key Fields:
externalId: Your platform's unique product identifierdeleteNotIncluded: If true, products not in the sync request will be deletedvariants: Array of product variants with their own external IDs
Step 2: Individual Product Operations​
Create a Single Product​
Endpoint: POST /api/v1/sdk/products
{
"externalId": "shopify_54321",
"title": "Classic Jeans",
"description": "Comfortable denim jeans",
"status": "Active",
"variants": [
{
"externalId": "shopify_variant_100",
"title": "32x32",
"sku": "JEANS-32-32",
"price": 79.99,
"inventoryQuantity": 50,
"inventoryTracking": true
}
]
}
Update a Product​
Endpoint: PUT /api/v1/sdk/products/{productId}
You can use either the internal VesuvioPay product ID (UUID) or the external ID:
{
"title": "Classic Denim Jeans - Updated",
"description": "Premium quality denim jeans",
"status": "Active"
}
Examples:
# Using internal ID
PUT /api/v1/sdk/products/a3f2b1c0-1234-5678-90ab-cdef12345678
# Using external ID
PUT /api/v1/sdk/products/shopify_54321
Delete a Product​
Endpoint: DELETE /api/v1/sdk/products/{productId}
DELETE /api/v1/sdk/products/shopify_54321
Get Product Details​
Endpoint: GET /api/v1/sdk/products/{productId}
GET /api/v1/sdk/products/shopify_54321
Step 3: Inventory Management​
Update inventory for a specific product variant in real-time.
Endpoint: PUT /api/v1/sdk/products/variants/{variantId}/inventory
{
"inventoryQuantity": 75,
"inventoryTracking": true
}
Response:
{
"success": true,
"data": {
"variantId": "b4e3c2d1-2345-6789-01bc-def123456789",
"externalVariantId": "shopify_variant_100",
"previousQuantity": 50,
"newQuantity": 75,
"updatedAt": "2025-10-08T10:30:00Z"
}
}
You can use either the internal variant ID or external variant ID in the URL.
Step 4: Linking Existing Products​
If you already have products in VesuvioPay and want to link them to your external platform:
Link Product to External Platform​
Endpoint: PUT /api/v1/sdk/products/{productId}/link-external
Note: This endpoint requires the internal VesuvioPay product ID (UUID), not the external ID.
{
"externalId": "shopify_99999",
"platformName": "Shopify"
}
Response:
{
"success": true,
"data": {
"productId": "a3f2b1c0-1234-5678-90ab-cdef12345678",
"externalId": "shopify_99999",
"platformName": "Shopify",
"linkedAt": "2025-10-08T10:30:00Z"
}
}
Link Product Variant to External Platform​
Endpoint: PUT /api/v1/sdk/products/{productId}/variants/{variantId}/link-external
{
"externalVariantId": "shopify_variant_999",
"platformName": "Shopify"
}
Handling Variants and Options​
Understanding Product Options​
Product options (like Size, Color, Material) are automatically created based on the variant options you provide:
{
"variants": [
{
"title": "Small / Red / Cotton",
"options": [
{ "name": "Size", "value": "Small" },
{ "name": "Color", "value": "Red" },
{ "name": "Material", "value": "Cotton" }
]
}
]
}
VesuvioPay will:
- Create product options (Size, Color, Material)
- Create option values (Small, Red, Cotton)
- Associate the variant with these option values
Variant Title Best Practices​
- If you don't specify a
title, it will be auto-generated from option values - Keep variant titles concise and descriptive
- Include key differentiating factors (size, color, etc.)
Best Practices for Bulk Sync​
1. Batch Size​
For optimal performance, sync products in batches:
- Recommended batch size: 100-500 products per request
- For large catalogs (10,000+ products), break into multiple requests
// Example: Batch processing
async function syncAllProducts(products) {
const batchSize = 250;
for (let i = 0; i < products.length; i += batchSize) {
const batch = products.slice(i, i + batchSize);
await fetch('https://api.vesuviopay.com/api/v1/sdk/products/sync', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify({ products: batch })
});
// Optional: Add delay between batches to avoid rate limits
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
2. Error Handling​
Always check the sync response for failed products:
const response = await syncProducts(batch);
if (response.data.failed > 0) {
response.data.results
.filter(r => r.status === 'failed')
.forEach(failed => {
console.error(`Failed to sync product ${failed.externalId}: ${failed.error}`);
});
}
3. Incremental Updates​
For real-time sync, only send changed products:
// Track last sync timestamp
const lastSync = getLastSyncTimestamp();
// Get products modified since last sync
const changedProducts = await getProductsModifiedSince(lastSync);
// Sync only changed products
await syncProducts(changedProducts);
// Update sync timestamp
setLastSyncTimestamp(new Date());
4. Inventory Sync Strategy​
For inventory updates, use the dedicated inventory endpoint instead of full product sync:
// Efficient inventory update
async function updateInventory(variantId, newQuantity) {
await fetch(`https://api.vesuviopay.com/api/v1/sdk/products/variants/${variantId}/inventory`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'your-private-api-key'
},
body: JSON.stringify({
inventoryQuantity: newQuantity,
inventoryTracking: true
})
});
}
5. Delete Strategy​
Use deleteNotIncluded: false for safety:
- Set to
trueonly for full catalog replacements - For incremental syncs, explicitly delete products using the DELETE endpoint
Complete Integration Example​
Here's a complete example of integrating product sync with Shopify:
const VESUVIO_API_KEY = process.env.VESUVIO_PRIVATE_API_KEY;
const VESUVIO_BASE_URL = 'https://api.vesuviopay.com/api/v1';
class VesuvioProductSync {
constructor(apiKey) {
this.apiKey = apiKey;
}
async syncProductsFromShopify(shopifyProducts) {
// Transform Shopify products to VesuvioPay format
const vesuvioProducts = shopifyProducts.map(product => ({
externalId: `shopify_${product.id}`,
title: product.title,
description: product.body_html,
vendor: product.vendor,
productType: product.product_type,
tags: product.tags.split(',').map(t => t.trim()),
status: product.status === 'active' ? 'Active' : 'Inactive',
images: product.images.map((img, index) => ({
url: img.src,
altText: img.alt,
position: index + 1
})),
variants: product.variants.map(variant => ({
externalId: `shopify_variant_${variant.id}`,
title: variant.title,
sku: variant.sku,
barcode: variant.barcode,
price: parseFloat(variant.price),
compareAtPrice: variant.compare_at_price ? parseFloat(variant.compare_at_price) : null,
inventoryQuantity: variant.inventory_quantity,
inventoryTracking: variant.inventory_management === 'shopify',
weight: variant.weight,
weightUnit: variant.weight_unit,
options: [
{ name: 'Option1', value: variant.option1 },
{ name: 'Option2', value: variant.option2 },
{ name: 'Option3', value: variant.option3 }
].filter(opt => opt.value)
}))
}));
// Sync in batches
const batchSize = 250;
const results = [];
for (let i = 0; i < vesuvioProducts.length; i += batchSize) {
const batch = vesuvioProducts.slice(i, i + batchSize);
try {
const response = await fetch(`${VESUVIO_BASE_URL}/sdk/products/sync`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
products: batch,
deleteNotIncluded: false
})
});
const data = await response.json();
results.push(data);
console.log(`Synced batch ${i / batchSize + 1}: ${data.data.created} created, ${data.data.updated} updated`);
// Rate limiting: wait 1 second between batches
if (i + batchSize < vesuvioProducts.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
} catch (error) {
console.error(`Error syncing batch ${i / batchSize + 1}:`, error);
}
}
return results;
}
async updateInventory(externalVariantId, newQuantity) {
try {
const response = await fetch(
`${VESUVIO_BASE_URL}/sdk/products/variants/${externalVariantId}/inventory`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
inventoryQuantity: newQuantity,
inventoryTracking: true
})
}
);
return await response.json();
} catch (error) {
console.error(`Error updating inventory for variant ${externalVariantId}:`, error);
throw error;
}
}
async linkExistingProduct(vesuvioProductId, externalId) {
try {
const response = await fetch(
`${VESUVIO_BASE_URL}/sdk/products/${vesuvioProductId}/link-external`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': this.apiKey
},
body: JSON.stringify({
externalId: externalId,
platformName: 'Shopify'
})
}
);
return await response.json();
} catch (error) {
console.error(`Error linking product ${vesuvioProductId}:`, error);
throw error;
}
}
}
// Usage
const syncService = new VesuvioProductSync(VESUVIO_API_KEY);
// Full catalog sync
const shopifyProducts = await fetchAllShopifyProducts();
await syncService.syncProductsFromShopify(shopifyProducts);
// Real-time inventory update
await syncService.updateInventory('shopify_variant_12345', 100);
Error Handling​
Common errors and how to handle them:
400 Bad Request​
Invalid product data. Check the response message for specific validation errors.
{
"success": false,
"message": "Validation failed",
"errors": [
"Product title is required",
"Variant price must be greater than 0"
]
}
401 Unauthorized​
Invalid or missing API key.
404 Not Found​
Product or variant not found. The external ID may not exist in VesuvioPay.
409 Conflict​
External ID already exists for a different product.
Next Steps​
- Set up Webhook Integration to receive product update notifications
- Explore Cart Integration to enable shopping with your synced products
- Review Order Management to handle purchases