Update Product
The Update Product endpoint enables you to modify existing product information including pricing, inventory, descriptions, images, and availability status. This endpoint supports partial updates, allowing you to change only specific fields while preserving others. Image uploads are supported through multipart/form-data requests.
Endpoint DetailsCopied!
-
Method:
PUT
-
URL:
/api/v0/products/{id}
-
Content-Type:
multipart/form-data
(for image uploads) orapplication/json
-
Authentication: Required (API Key & Secret)
-
Idempotency: Supported (recommended for update operations)
Path ParametersCopied!
Parameter |
Type |
Required |
Description |
Example |
---|---|---|---|---|
|
string (UUID) |
Yes |
Unique product identifier |
|
Request ParametersCopied!
Optional Fields (Partial Update Support)
Field |
Type |
Description |
Example |
---|---|---|---|
|
string |
Product name (1-200 characters) |
|
|
string |
Product description (max 2000 characters) |
|
|
number |
Product price (must be > 0.01) |
|
|
string |
3-letter ISO currency code |
|
|
string |
Product classification |
|
|
enum |
Product category ( |
|
|
enum |
Product status ( |
|
|
number |
Product weight for shipping |
|
|
string |
Unit of measurement |
|
|
number |
Available quantity |
|
|
number |
Current stock level |
|
|
array |
Product image files or URLs |
See examples below |
Validation Rules
-
Name: 1-200 characters, must be unique within your product catalog
-
Description: Maximum 2000 characters, supports markdown formatting
-
Price: Must be greater than 0.01, supports up to 2 decimal places
-
Currency: Must be valid ISO 3-letter code (USD, EUR, USDC, EURC)
-
Images: Maximum 10 images, 10MB per image, JPEG/PNG/WebP formats
-
Stock Count: Must be >= 0 for physical products
-
Weight: Must be > 0 if specified
Request ExamplesCopied!
Basic Product Update (JSON)
curl -X PUT "https://api.devdraft.com/api/v0/products/550e8400-e29b-41d4-a716-446655440000" \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "x-client-secret: YOUR_CLIENT_SECRET" \
-H "x-idempotency-key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Software License Pro",
"price": 399.99,
"description": "Enhanced annual license with premium features, priority support, and advanced analytics dashboard."
}'
Price and Status Update
curl -X PUT "https://api.devdraft.com/api/v0/products/550e8400-e29b-41d4-a716-446655440000" \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "x-client-secret: YOUR_CLIENT_SECRET" \
-H "x-idempotency-key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"price": 249.99,
"status": "ACTIVE"
}'
Inventory Update
curl -X PUT "https://api.devdraft.com/api/v0/products/550e8400-e29b-41d4-a716-446655440001" \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "x-client-secret: YOUR_CLIENT_SECRET" \
-H "x-idempotency-key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"stockCount": 200,
"quantity": 200,
"status": "ACTIVE"
}'
Update with New Images
curl -X PUT "https://api.devdraft.com/api/v0/products/550e8400-e29b-41d4-a716-446655440000" \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "x-client-secret: YOUR_CLIENT_SECRET" \
-H "x-idempotency-key: $(uuidgen)" \
-F "name=Gaming Laptop Pro" \
-F "price=1599.99" \
-F "description=Updated high-performance gaming laptop with latest RTX graphics" \
-F "images=@/path/to/new-laptop1.jpg" \
-F "images=@/path/to/new-laptop2.jpg" \
-F "stockCount=15"
Product Type Change
curl -X PUT "https://api.devdraft.com/api/v0/products/550e8400-e29b-41d4-a716-446655440002" \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "x-client-secret: YOUR_CLIENT_SECRET" \
-H "x-idempotency-key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"productType": "SERVICE",
"unit": "hour",
"quantity": 50,
"stockCount": null
}'
JavaScript/Node.js Examples
// Basic product update
const updateProduct = async (productId, updates) => {
try {
const response = await fetch(`https://api.devdraft.com/api/v0/products/${productId}`, {
method: 'PUT',
headers: {
'x-client-key': 'YOUR_CLIENT_KEY',
'x-client-secret': 'YOUR_CLIENT_SECRET',
'x-idempotency-key': generateUUID(),
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const updatedProduct = await response.json();
return updatedProduct;
} catch (error) {
console.error('Error updating product:', error);
throw error;
}
};
// Update product with images
const updateProductWithImages = async (productId, updates, imageFiles = []) => {
const formData = new FormData();
// Add text fields
Object.keys(updates).forEach(key => {
if (key !== 'images') {
formData.append(key, updates[key]);
}
});
// Add image files
imageFiles.forEach(file => {
formData.append('images', file);
});
try {
const response = await fetch(`https://api.devdraft.com/api/v0/products/${productId}`, {
method: 'PUT',
headers: {
'x-client-key': 'YOUR_CLIENT_KEY',
'x-client-secret': 'YOUR_CLIENT_SECRET',
'x-idempotency-key': generateUUID()
// Don't set Content-Type for FormData - browser will set it with boundary
},
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error updating product with images:', error);
throw error;
}
};
// Usage examples
try {
// Simple price update
const priceUpdate = await updateProduct('550e8400-e29b-41d4-a716-446655440000', {
price: 299.99,
currency: 'USD'
});
// Inventory management
const inventoryUpdate = await updateProduct('550e8400-e29b-41d4-a716-446655440001', {
stockCount: 150,
status: 'ACTIVE'
});
// Product rebranding
const rebrandUpdate = await updateProduct('550e8400-e29b-41d4-a716-446655440002', {
name: 'New Product Name',
description: 'Updated product description with new branding',
price: 199.99
});
console.log('Products updated successfully');
} catch (error) {
console.error('Update failed:', error);
}
Python Example
import requests
import json
def update_product(product_id, updates, client_key, client_secret, images=None):
url = f"https://api.devdraft.com/api/v0/products/{product_id}"
headers = {
'x-client-key': client_key,
'x-client-secret': client_secret,
'x-idempotency-key': str(uuid.uuid4())
}
if images:
# Use multipart/form-data for image uploads
files = []
data = {}
# Add text fields
for key, value in updates.items():
if key != 'images':
data[key] = value
# Add image files
for image_path in images:
files.append(('images', open(image_path, 'rb')))
try:
response = requests.put(url, headers=headers, data=data, files=files)
response.raise_for_status()
return response.json()
finally:
# Close file handles
for _, file_handle in files:
file_handle.close()
else:
# Use JSON for simple updates
headers['Content-Type'] = 'application/json'
response = requests.put(url, headers=headers, json=updates)
response.raise_for_status()
return response.json()
# Usage examples
try:
# Price and description update
updated_product = update_product(
"550e8400-e29b-41d4-a716-446655440000",
{
"price": 349.99,
"description": "Updated description with new features"
},
"YOUR_CLIENT_KEY",
"YOUR_CLIENT_SECRET"
)
print(f"Updated product: {updated_product['name']}")
except requests.exceptions.RequestException as e:
print(f"Error updating product: {e}")
Response FormatCopied!
Success Response (200 OK)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Premium Software License Pro",
"description": "Enhanced annual license with premium features, priority support, and advanced analytics dashboard.",
"price": 399.99,
"currency": "USD",
"productType": "PRODUCT",
"status": "ACTIVE",
"stockCount": null,
"quantity": null,
"weight": null,
"unit": null,
"images": [
"https://devdraft-images.s3.amazonaws.com/products/software-license-main.jpg",
"https://devdraft-images.s3.amazonaws.com/products/software-license-features.jpg"
],
"variations": null,
"paymentLink": "https://pay.devdraft.com/p/premium-license-pro",
"walletId": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
"dateAdded": "2024-01-15T10:30:00.000Z",
"dateUpdated": "2024-01-20T14:45:00.000Z",
"wallet": {
"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
"address": "0x742d35Cc6635C0532925a3b8d",
"blockchain": "ETHEREUM",
"type": "APP"
},
"transactions": [
{
"id": "txn_123456789",
"amount": 399.99,
"currency": "USD",
"status": "PAYMENT_PROCESSED",
"dateCreated": "2024-01-20T15:00:00.000Z"
}
]
}
Inventory Update Response
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"name": "Wireless Bluetooth Headphones",
"description": "Premium wireless headphones with active noise cancellation, 30-hour battery life, and superior sound quality.",
"price": 199.99,
"currency": "USD",
"productType": "PRODUCT",
"status": "ACTIVE",
"stockCount": 200,
"quantity": 200,
"weight": 0.5,
"unit": "kg",
"images": [
"https://devdraft-images.s3.amazonaws.com/products/headphones-main.jpg"
],
"dateAdded": "2024-01-14T14:20:00.000Z",
"dateUpdated": "2024-01-20T16:30:00.000Z",
"walletId": "abcd1234-5678-90ef-ghij-klmnopqrstuv"
}
Error ResponsesCopied!
Validation Error (400 Bad Request)
{
"statusCode": 400,
"message": [
"Price must be greater than 0",
"Product name cannot exceed 200 characters",
"Currency must be a valid 3-letter ISO code"
],
"error": "Bad Request"
}
Product Not Found (404 Not Found)
{
"statusCode": 404,
"message": "Product not found",
"error": "Not Found"
}
Invalid Product ID (400 Bad Request)
{
"statusCode": 400,
"message": "Invalid product ID format. Must be a valid UUID.",
"error": "Bad Request"
}
Authentication Error (401 Unauthorized)
{
"statusCode": 401,
"message": "Invalid or missing API credentials",
"error": "Unauthorized"
}
Image Upload Error (400 Bad Request)
{
"statusCode": 400,
"message": "Image file size exceeds 10MB limit",
"error": "Bad Request"
}
Rate Limit Error (429 Too Many Requests)
{
"statusCode": 429,
"message": "Rate limit exceeded. Maximum 100 requests per minute.",
"error": "Too Many Requests",
"retryAfter": 60
}
Business Logic & BehaviorCopied!
Partial Updates
-
Only provided fields are updated; omitted fields remain unchanged
-
null
values explicitly set fields to null -
Empty strings are treated as valid values (will clear the field)
Image Handling
-
New images are uploaded to cloud storage and URLs are generated
-
Existing images are preserved unless explicitly replaced
-
Images are automatically optimized and resized
-
Multiple formats are generated for different use cases
Inventory Management
-
Stock count changes trigger inventory log entries
-
Automatic status changes based on stock levels (configurable)
-
Physical products require stock tracking; digital products are optional
Wallet Assignment
-
Products maintain their existing wallet assignments
-
If app's default wallet changes, products are not automatically reassigned
-
Wallet changes require explicit update requests
Price History
-
Price changes are tracked in the audit trail
-
oldPrice
field can be used to show discounts and promotions -
Currency changes preserve the numeric value but update the currency code
Advanced Use CasesCopied!
Bulk Product Updates
// Update multiple products efficiently
const bulkUpdateProducts = async (updates) => {
const results = [];
const batchSize = 10;
for (let i = 0; i < updates.length; i += batchSize) {
const batch = updates.slice(i, i + batchSize);
const batchPromises = batch.map(({ productId, changes }) =>
updateProduct(productId, changes)
.then(result => ({ success: true, productId, result }))
.catch(error => ({ success: false, productId, error: error.message }))
);
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
// Rate limiting consideration
if (i + batchSize < updates.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
};
// Usage
const productUpdates = [
{ productId: 'prod-1', changes: { price: 199.99, status: 'ACTIVE' } },
{ productId: 'prod-2', changes: { stockCount: 150 } },
{ productId: 'prod-3', changes: { description: 'New description' } }
];
const results = await bulkUpdateProducts(productUpdates);
console.log(`Updated ${results.filter(r => r.success).length} products`);
Dynamic Pricing Updates
// Implement dynamic pricing strategy
const applyDynamicPricing = async (productId, strategy) => {
try {
// Get current product data
const product = await fetchProduct(productId);
let newPrice = product.price;
switch (strategy.type) {
case 'DISCOUNT':
newPrice = product.price * (1 - strategy.percentage / 100);
break;
case 'MARKUP':
newPrice = product.price * (1 + strategy.percentage / 100);
break;
case 'FIXED_PRICE':
newPrice = strategy.price;
break;
case 'COMPETITIVE':
// Implement competitive pricing logic
newPrice = calculateCompetitivePrice(product, strategy.competitors);
break;
}
// Round to 2 decimal places
newPrice = Math.round(newPrice * 100) / 100;
// Update product with new price
const updates = {
price: newPrice,
oldPrice: product.price // Store original price for reference
};
return await updateProduct(productId, updates);
} catch (error) {
console.error('Dynamic pricing update failed:', error);
throw error;
}
};
Inventory Automation
// Automatic stock management
const automateInventory = async (productId, rules) => {
const product = await fetchProduct(productId);
const updates = {};
// Low stock alert threshold
if (product.stockCount <= rules.lowStockThreshold) {
console.warn(`Low stock alert for ${product.name}: ${product.stockCount} remaining`);
// Auto-reorder if enabled
if (rules.autoReorder && product.stockCount <= rules.reorderPoint) {
updates.stockCount = product.stockCount + rules.reorderQuantity;
console.info(`Auto-reordering ${rules.reorderQuantity} units for ${product.name}`);
}
}
// Out of stock management
if (product.stockCount === 0 && rules.deactivateWhenEmpty) {
updates.status = 'INACTIVE';
console.info(`Deactivating out-of-stock product: ${product.name}`);
}
// Back in stock activation
if (product.stockCount > 0 && product.status === 'INACTIVE' && rules.reactivateWhenStocked) {
updates.status = 'ACTIVE';
console.info(`Reactivating restocked product: ${product.name}`);
}
// Apply updates if any
if (Object.keys(updates).length > 0) {
return await updateProduct(productId, updates);
}
return product;
};
Seasonal Pricing
// Implement seasonal pricing adjustments
const applySeasonalPricing = async (productIds, season) => {
const seasonalRules = {
'SUMMER': { discount: 15, categories: ['winter-clothing', 'heaters'] },
'WINTER': { markup: 20, categories: ['winter-clothing', 'heaters'] },
'HOLIDAY': { discount: 25, categories: ['electronics', 'toys'] },
'BACK_TO_SCHOOL': { discount: 10, categories: ['electronics', 'books'] }
};
const rule = seasonalRules[season];
if (!rule) return;
const updates = productIds.map(async (productId) => {
try {
const product = await fetchProduct(productId);
// Check if product category matches seasonal rule
const categoryMatch = rule.categories.some(cat =>
product.name.toLowerCase().includes(cat) ||
product.description.toLowerCase().includes(cat)
);
if (!categoryMatch) return null;
let newPrice;
if (rule.discount) {
newPrice = product.price * (1 - rule.discount / 100);
} else if (rule.markup) {
newPrice = product.price * (1 + rule.markup / 100);
}
return await updateProduct(productId, {
price: Math.round(newPrice * 100) / 100,
oldPrice: product.price
});
} catch (error) {
console.error(`Failed to apply seasonal pricing to ${productId}:`, error);
return null;
}
});
const results = await Promise.all(updates);
return results.filter(result => result !== null);
};
Integration PatternsCopied!
Update Validation Wrapper
// Validate updates before sending to API
const validateProductUpdate = (updates) => {
const errors = [];
if (updates.name && (updates.name.length === 0 || updates.name.length > 200)) {
errors.push('Product name must be between 1 and 200 characters');
}
if (updates.price && updates.price <= 0) {
errors.push('Price must be greater than 0');
}
if (updates.stockCount && updates.stockCount < 0) {
errors.push('Stock count cannot be negative');
}
if (updates.currency && !/^[A-Z]{3}$/.test(updates.currency)) {
errors.push('Currency must be a valid 3-letter ISO code');
}
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(', ')}`);
}
return true;
};
// Safe update wrapper
const safeUpdateProduct = async (productId, updates) => {
try {
validateProductUpdate(updates);
return await updateProduct(productId, updates);
} catch (error) {
console.error('Product update validation failed:', error);
throw error;
}
};
Audit Trail Integration
// Track all product changes for audit purposes
const updateProductWithAudit = async (productId, updates, userId) => {
try {
// Get original product for comparison
const originalProduct = await fetchProduct(productId);
// Perform update
const updatedProduct = await updateProduct(productId, updates);
// Log changes
const changes = {};
Object.keys(updates).forEach(key => {
if (originalProduct[key] !== updatedProduct[key]) {
changes[key] = {
from: originalProduct[key],
to: updatedProduct[key]
};
}
});
// Send to audit system
await logAuditEvent({
action: 'PRODUCT_UPDATE',
entityId: productId,
entityType: 'PRODUCT',
userId: userId,
changes: changes,
timestamp: new Date().toISOString()
});
return updatedProduct;
} catch (error) {
console.error('Product update with audit failed:', error);
throw error;
}
};
Best PracticesCopied!
Error Handling
-
Always validate input before sending requests
-
Implement retry logic for network failures
-
Handle specific error codes appropriately
-
Log errors for monitoring and debugging
Performance
-
Use partial updates to minimize data transfer
-
Batch operations when updating multiple products
-
Implement caching for frequently updated products
-
Consider rate limiting in bulk operations
Security
-
Validate all input parameters
-
Use idempotency keys for critical updates
-
Implement proper authentication and authorization
-
Sanitize user-provided content
Data Integrity
-
Verify updates by fetching the updated product
-
Implement transaction-like behavior for related updates
-
Use optimistic locking for concurrent updates
-
Maintain backup of critical product data
Next StepsCopied!
After updating a product, you can:
-
Verify Changes: Fetch the updated product to confirm changes
-
Update Related Resources: Sync changes to payment links and invoices
-
Notify Customers: Send updates about price or availability changes
-
Monitor Performance: Track how updates affect sales and engagement
-
Automate Workflows: Set up triggers for inventory management
For more information, see: