Delete Product
The Delete Product endpoint permanently removes a product from your catalog. This operation is irreversible and will delete all product data including images, variations, and associated metadata. The product will no longer be available for purchase or inclusion in payment links and invoices.
Endpoint DetailsCopied!
-
Method:
DELETE
-
URL:
/api/v0/products/{id}
-
Authentication: Required (API Key & Secret)
-
Rate Limiting: 100 requests per minute
-
Idempotency: Supported (recommended for delete operations)
Path ParametersCopied!
Parameter |
Type |
Required |
Description |
Example |
---|---|---|---|---|
|
string (UUID) |
Yes |
Unique product identifier |
|
Request ExamplesCopied!
Basic Product Deletion
curl -X DELETE "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)"
JavaScript/Node.js Example
const deleteProduct = async (productId) => {
try {
const response = await fetch(`https://api.devdraft.com/api/v0/products/${productId}`, {
method: 'DELETE',
headers: {
'x-client-key': 'YOUR_CLIENT_KEY',
'x-client-secret': 'YOUR_CLIENT_SECRET',
'x-idempotency-key': generateUUID()
}
});
if (!response.ok) {
if (response.status === 404) {
throw new Error('Product not found');
}
if (response.status === 409) {
throw new Error('Product cannot be deleted due to existing references');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const deletedProduct = await response.json();
return deletedProduct;
} catch (error) {
console.error('Error deleting product:', error);
throw error;
}
};
// Usage with confirmation
const handleProductDeletion = async (productId, productName) => {
// Always confirm before deletion
const confirmDelete = confirm(
`Are you sure you want to delete "${productName}"? This action cannot be undone.`
);
if (!confirmDelete) {
return null;
}
try {
const deletedProduct = await deleteProduct(productId);
console.log(`Successfully deleted product: ${deletedProduct.name}`);
return deletedProduct;
} catch (error) {
console.error('Failed to delete product:', error.message);
throw error;
}
};
// Example usage
try {
const result = await handleProductDeletion(
'550e8400-e29b-41d4-a716-446655440000',
'Premium Software License'
);
if (result) {
// Update UI to remove product from list
removeProductFromUI(result.id);
}
} catch (error) {
alert('Failed to delete product: ' + error.message);
}
Python Example
import requests
import uuid
def delete_product(product_id, client_key, client_secret, confirm=True):
"""Delete a product with optional confirmation"""
if confirm:
response = input(f"Are you sure you want to delete product {product_id}? (yes/no): ")
if response.lower() != 'yes':
print("Deletion cancelled")
return 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())
}
try:
response = requests.delete(url, headers=headers)
if response.status_code == 404:
raise ValueError("Product not found")
elif response.status_code == 409:
raise ValueError("Product cannot be deleted due to existing references")
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"Failed to delete product: {e}")
# Usage example
try:
deleted_product = delete_product(
"550e8400-e29b-41d4-a716-446655440000",
"YOUR_CLIENT_KEY",
"YOUR_CLIENT_SECRET"
)
if deleted_product:
print(f"Successfully deleted: {deleted_product['name']}")
except Exception as e:
print(f"Error: {e}")
React Component Example
import React, { useState } from 'react';
const ProductDeletionModal = ({ product, onDelete, onCancel }) => {
const [isDeleting, setIsDeleting] = useState(false);
const [confirmText, setConfirmText] = useState('');
const handleDelete = async () => {
// Require typing product name for confirmation
if (confirmText !== product.name) {
alert('Please type the exact product name to confirm deletion');
return;
}
setIsDeleting(true);
try {
await deleteProduct(product.id);
onDelete(product);
} catch (error) {
alert('Failed to delete product: ' + error.message);
} finally {
setIsDeleting(false);
}
};
return (
<div className="modal-overlay">
<div className="modal-content">
<h2>Delete Product</h2>
<div className="warning-box">
<p><strong>⚠️ Warning:</strong> This action cannot be undone.</p>
<p>Deleting this product will:</p>
<ul>
<li>Permanently remove all product data</li>
<li>Delete associated images from storage</li>
<li>Remove the product from all payment links</li>
<li>Prevent future sales of this product</li>
</ul>
</div>
<div className="product-info">
<h3>Product to Delete:</h3>
<p><strong>Name:</strong> {product.name}</p>
<p><strong>Price:</strong> ${product.price}</p>
<p><strong>Status:</strong> {product.status}</p>
</div>
<div className="confirmation-input">
<label>
Type "<strong>{product.name}</strong>" to confirm deletion:
<input
type="text"
value={confirmText}
onChange={(e) => setConfirmText(e.target.value)}
placeholder="Type product name here"
disabled={isDeleting}
/>
</label>
</div>
<div className="modal-actions">
<button
onClick={onCancel}
disabled={isDeleting}
className="btn-cancel"
>
Cancel
</button>
<button
onClick={handleDelete}
disabled={isDeleting || confirmText !== product.name}
className="btn-delete"
>
{isDeleting ? 'Deleting...' : 'Delete Product'}
</button>
</div>
</div>
</div>
);
};
Response FormatCopied!
Success Response (200 OK)
The response returns the deleted product's final state before removal:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Premium Software License",
"description": "Annual license for our premium software suite with advanced features, priority support, and regular updates.",
"price": 299.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",
"walletId": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
"dateAdded": "2024-01-15T10:30:00.000Z",
"dateUpdated": "2024-01-15T10:30:00.000Z",
"wallet": {
"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv",
"address": "0x742d35Cc6635C0532925a3b8d",
"blockchain": "ETHEREUM",
"type": "APP"
},
"transactions": [
{
"id": "txn_123456789",
"amount": 299.99,
"currency": "USD",
"status": "PAYMENT_PROCESSED",
"dateCreated": "2024-01-15T11:00:00.000Z"
}
]
}
Error ResponsesCopied!
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"
}
Foreign Key Constraint (409 Conflict)
{
"statusCode": 409,
"message": "Cannot delete product. Product is referenced by existing invoices, payment links, or transactions.",
"error": "Conflict",
"details": {
"references": [
"5 active payment links",
"12 pending invoices",
"48 completed transactions"
]
}
}
Authentication Error (401 Unauthorized)
{
"statusCode": 401,
"message": "Invalid or missing API credentials",
"error": "Unauthorized"
}
Rate Limit Error (429 Too Many Requests)
{
"statusCode": 429,
"message": "Rate limit exceeded. Maximum 100 requests per minute.",
"error": "Too Many Requests",
"retryAfter": 60
}
Insufficient Permissions (403 Forbidden)
{
"statusCode": 403,
"message": "Insufficient permissions to delete products",
"error": "Forbidden"
}
Business Logic & ConstraintsCopied!
Deletion Rules
-
Product Ownership: Can only delete products belonging to your application
-
Foreign Key Constraints: Products with existing references cannot be deleted
-
Transaction History: Products with completed transactions are preserved in transaction records
-
Image Cleanup: Product images are automatically removed from cloud storage
-
Payment Link Updates: Active payment links are automatically updated to remove the product
Referenced Data Handling
Reference Type |
Behavior |
Impact |
---|---|---|
Active Payment Links |
Product removed from payment links |
Payment links continue to work with remaining products |
Pending Invoices |
Deletion blocked |
Must resolve or cancel invoices first |
Completed Transactions |
Transaction history preserved |
Product reference maintained for historical records |
Draft Invoices |
Product removed automatically |
Invoices updated to exclude deleted product |
Product Images |
Automatically deleted |
Images removed from cloud storage |
Audit Trail
-
All product deletions are logged in the audit trail
-
Includes user information, timestamp, and product details
-
Provides accountability and compliance tracking
-
Cannot be modified or deleted
Advanced Use CasesCopied!
Safe Deletion with Validation
// Comprehensive deletion validation
const safeDeleteProduct = async (productId) => {
try {
// 1. Fetch product details first
const product = await fetchProduct(productId);
// 2. Check for active references
const references = await checkProductReferences(productId);
if (references.hasActiveReferences) {
throw new Error(
`Cannot delete product. Found active references: ${references.details.join(', ')}`
);
}
// 3. Confirm deletion with user
const confirmed = await confirmDeletion(product);
if (!confirmed) {
return null;
}
// 4. Create backup before deletion (optional)
await createProductBackup(product);
// 5. Perform deletion
const deletedProduct = await deleteProduct(productId);
// 6. Clean up related data
await cleanupProductReferences(productId);
return deletedProduct;
} catch (error) {
console.error('Safe deletion failed:', error);
throw error;
}
};
// Check for product references
const checkProductReferences = async (productId) => {
const checks = await Promise.all([
checkPaymentLinks(productId),
checkPendingInvoices(productId),
checkActiveTransactions(productId)
]);
const activeReferences = checks.filter(check => check.hasReferences);
return {
hasActiveReferences: activeReferences.length > 0,
details: activeReferences.map(ref => ref.description)
};
};
Bulk Product Deletion
// Delete multiple products with safety checks
const bulkDeleteProducts = async (productIds, options = {}) => {
const { confirmAll = true, skipOnError = false } = options;
const results = [];
// Validate all products first
const validationResults = await Promise.all(
productIds.map(async (id) => {
try {
const product = await fetchProduct(id);
const references = await checkProductReferences(id);
return { id, product, references, valid: !references.hasActiveReferences };
} catch (error) {
return { id, error: error.message, valid: false };
}
})
);
const validProducts = validationResults.filter(r => r.valid);
const invalidProducts = validationResults.filter(r => !r.valid);
if (invalidProducts.length > 0) {
console.warn(`${invalidProducts.length} products cannot be deleted due to constraints`);
if (!skipOnError) {
throw new Error(
`Cannot delete products with active references: ${invalidProducts.map(p => p.id).join(', ')}`
);
}
}
// Confirm bulk deletion
if (confirmAll && validProducts.length > 0) {
const confirmed = confirm(
`Delete ${validProducts.length} products? This action cannot be undone.`
);
if (!confirmed) {
return { deleted: [], skipped: productIds.length, errors: [] };
}
}
// Delete valid products in batches
const batchSize = 10;
for (let i = 0; i < validProducts.length; i += batchSize) {
const batch = validProducts.slice(i, i + batchSize);
const batchResults = await Promise.all(
batch.map(async ({ id, product }) => {
try {
const deleted = await deleteProduct(id);
return { success: true, id, product: deleted };
} catch (error) {
return { success: false, id, error: error.message };
}
})
);
results.push(...batchResults);
// Rate limiting delay
if (i + batchSize < validProducts.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);
return {
deleted: successful.map(r => r.product),
skipped: invalidProducts.length,
errors: failed.map(r => ({ id: r.id, error: r.error })),
summary: `Deleted ${successful.length}, skipped ${invalidProducts.length}, failed ${failed.length}`
};
};
Product Archival Alternative
// Archive instead of delete for better data preservation
const archiveProduct = async (productId) => {
try {
// Set product to inactive status
const archivedProduct = await updateProduct(productId, {
status: 'INACTIVE',
// Add archive metadata
description: `[ARCHIVED] ${product.description}`,
// Move to archive category if using categories
categoryId: 'archived-products'
});
// Log archival action
await logAuditEvent({
action: 'PRODUCT_ARCHIVED',
entityId: productId,
timestamp: new Date().toISOString(),
metadata: { originalStatus: 'ACTIVE' }
});
return archivedProduct;
} catch (error) {
console.error('Archival failed:', error);
throw error;
}
};
// Restore archived product
const restoreProduct = async (productId) => {
const product = await fetchProduct(productId);
return await updateProduct(productId, {
status: 'ACTIVE',
description: product.description.replace('[ARCHIVED] ', ''),
categoryId: null // Remove from archive category
});
};
Deletion with Data Export
// Export product data before deletion
const deleteWithExport = async (productId, exportFormat = 'json') => {
try {
// 1. Fetch complete product data
const product = await fetchProduct(productId);
// 2. Gather related data
const relatedData = await gatherProductData(productId);
// 3. Create export package
const exportData = {
product,
transactions: relatedData.transactions,
analytics: relatedData.analytics,
images: relatedData.images,
exportedAt: new Date().toISOString(),
reason: 'Product deletion backup'
};
// 4. Save export
const exportFile = await saveExport(exportData, exportFormat);
console.log(`Product data exported to: ${exportFile}`);
// 5. Perform deletion
const deletedProduct = await deleteProduct(productId);
return {
deleted: deletedProduct,
export: exportFile,
exportData: exportData
};
} catch (error) {
console.error('Delete with export failed:', error);
throw error;
}
};
const gatherProductData = async (productId) => {
const [transactions, analytics] = await Promise.all([
getProductTransactions(productId),
getProductAnalytics(productId)
]);
return { transactions, analytics };
};
Integration PatternsCopied!
Error Handling & Recovery
// Robust deletion with error recovery
const deleteProductWithRecovery = async (productId, options = {}) => {
const { backup = true, retryOnFailure = true } = options;
let backupData = null;
try {
// Create backup if requested
if (backup) {
const product = await fetchProduct(productId);
backupData = await createProductBackup(product);
}
// Attempt deletion
const result = await deleteProduct(productId);
// Cleanup backup on success
if (backupData && backupData.cleanupOnSuccess) {
await cleanupBackup(backupData.id);
}
return result;
} catch (error) {
// Recovery logic
if (error.message.includes('Foreign key constraint') && retryOnFailure) {
console.warn('Deletion failed due to constraints, attempting cleanup...');
try {
await cleanupProductReferences(productId);
return await deleteProduct(productId); // Retry
} catch (retryError) {
console.error('Retry failed:', retryError);
throw retryError;
}
}
// Restore from backup if critical failure
if (backupData && error.message.includes('data corruption')) {
console.warn('Attempting to restore from backup...');
await restoreFromBackup(backupData);
}
throw error;
}
};
Audit and Compliance
// Comprehensive audit logging for deletions
const auditedDelete = async (productId, userId, reason = '') => {
const startTime = Date.now();
try {
// Pre-deletion audit
const product = await fetchProduct(productId);
await logAuditEvent({
action: 'PRODUCT_DELETE_INITIATED',
entityId: productId,
userId,
metadata: {
productName: product.name,
productValue: product.price,
reason,
timestamp: new Date().toISOString()
}
});
// Perform deletion
const deletedProduct = await deleteProduct(productId);
// Post-deletion audit
await logAuditEvent({
action: 'PRODUCT_DELETE_COMPLETED',
entityId: productId,
userId,
metadata: {
productName: deletedProduct.name,
duration: Date.now() - startTime,
success: true,
timestamp: new Date().toISOString()
}
});
return deletedProduct;
} catch (error) {
// Failure audit
await logAuditEvent({
action: 'PRODUCT_DELETE_FAILED',
entityId: productId,
userId,
metadata: {
error: error.message,
duration: Date.now() - startTime,
timestamp: new Date().toISOString()
}
});
throw error;
}
};
Security ConsiderationsCopied!
Access Control
// Verify permissions before deletion
const authorizedDelete = async (productId, userToken) => {
try {
// Verify user has delete permissions
const permissions = await verifyUserPermissions(userToken);
if (!permissions.includes('delete:products')) {
throw new Error('Insufficient permissions to delete products');
}
// Verify product ownership
const product = await fetchProduct(productId);
const userApps = await getUserApplications(userToken);
if (!userApps.includes(product.app_id)) {
throw new Error('Cannot delete product from different application');
}
return await deleteProduct(productId);
} catch (error) {
console.error('Authorization failed:', error);
throw error;
}
};
Data Protection
// Secure deletion with data wiping
const secureDelete = async (productId) => {
try {
// 1. Delete product record
const deletedProduct = await deleteProduct(productId);
// 2. Securely wipe image files
if (deletedProduct.images && deletedProduct.images.length > 0) {
await Promise.all(
deletedProduct.images.map(imageUrl => secureDeleteImage(imageUrl))
);
}
// 3. Clear any cached data
await clearProductCache(productId);
// 4. Notify related services
await notifyDeletionServices(productId);
return deletedProduct;
} catch (error) {
console.error('Secure deletion failed:', error);
throw error;
}
};
Best PracticesCopied!
Confirmation & Validation
-
Always confirm destructive operations with users
-
Validate permissions before allowing deletion
-
Check for dependencies that would prevent deletion
-
Provide clear warnings about irreversible consequences
Data Management
-
Create backups before deletion for recovery purposes
-
Export important data for compliance and historical records
-
Clean up related resources (images, cache entries)
-
Update dependent systems that reference the product
User Experience
-
Show deletion impact (number of affected items)
-
Provide alternatives like archiving for reversible removal
-
Give clear feedback about deletion success or failure
-
Offer bulk operations for efficiency when managing multiple products
Error Recovery
-
Implement retry logic for transient failures
-
Provide rollback capabilities where possible
-
Log all deletion attempts for audit and debugging
-
Handle constraint violations gracefully with helpful messages
Next StepsCopied!
After deleting a product, consider:
-
Update Related Systems: Remove product references from external systems
-
Clean Analytics Data: Archive or remove product performance data
-
Notify Stakeholders: Inform teams about product removal
-
Review Dependencies: Check for any missed references that need cleanup
-
Monitor Impact: Ensure deletion doesn't break existing workflows
For more information, see: