Products

Integrate Products

This guide provides practical implementation patterns for integrating CashIn's Product Management endpoints. These endpoints transform your inventory into intelligent, campaign-ready assets that drive viral growth and customer engagement through the PIIC framework.

Product Endpoints

Endpoint

Method

Purpose

Key Parameters

Response Data

/partner/v1/products/new

POST

Create new product with images

name, description, price, images[], metadata

product object, piic_summary, image_url

/partner/v1/products/list

GET

Retrieve all products with pagination

page, limit, is_active, category_id

products array, pagination object

/partner/v1/products/update

PUT

Update existing product and images

id, name, price, images[], remove_images

Updated product object, new piic_summary

/partner/v1/products/delete

DELETE

Remove product and category associations

id

Success confirmation

/partner/v1/products/assign-category

POST

Assign product to category

product_id, category_id

Success confirmation

/partner/v1/products/remove-category

DELETE

Remove product from category

product_id, category_id

Success confirmation

Category Endpoints

Endpoint

Method

Purpose

Key Parameters

Response Data

/partner/v1/category/new

POST

Create product category

name

category object, piic_summary

/partner/v1/category/list

GET

Retrieve all categories

include_products

categories array with optional products

/partner/v1/category/update

PUT

Update category name

id, name

Updated category object, new piic_summary

/partner/v1/category/delete

DELETE

Remove empty category

id

Success confirmation

/partner/v1/category/{category_id}/products

GET

Get products in specific category

category_id (URL param)

category object, products array

Authentication Setup

All Product Management endpoints require Bearer token authentication using your partner token.

const headers = {
  'Authorization': 'Bearer <partner_token>',
  'Content-Type': 'application/json'
};

// For multipart uploads (with images)
const formHeaders = {
  'Authorization': 'Bearer <partner_token>'
  // Content-Type is automatically set for multipart/form-data
};

Integration Patterns

Pattern 1: Complete Product Creation with Images

Handle product creation with image uploads and automatic PIIC summarization.

class ProductManager {
  constructor(partnerToken) {
    this.partnerToken = partnerToken;
    this.baseHeaders = {
      'Authorization': `Bearer ${partnerToken}`
    };
  }

  async createProduct(productData, imageFiles = []) {
    const formData = new FormData();
    
    // Add product data
    Object.keys(productData).forEach(key => {
      if (productData[key] !== undefined) {
        if (typeof productData[key] === 'object') {
          formData.append(key, JSON.stringify(productData[key]));
        } else {
          formData.append(key, productData[key]);
        }
      }
    });

    // Add image files
    imageFiles.forEach((file, index) => {
      if (this.validateImageFile(file)) {
        formData.append('images[]', file);
      } else {
        throw new Error(`Invalid image file at index ${index}: ${file.name}`);
      }
    });

    try {
      const response = await fetch('/partner/v1/products/new', {
        method: 'POST',
        headers: this.baseHeaders,
        body: formData
      });

      const result = await response.json();

      if (result.success) {
        console.log('Product created with PIIC summary:', result.data.piic_summary);
        return {
          success: true,
          product: result.data,
          piicSummary: result.data.piic_summary
        };
      } else {
        return { success: false, error: result.error, details: result.details };
      }
    } catch (error) {
      console.error('Product creation failed:', error);
      return { success: false, error: 'Network error during product creation' };
    }
  }

  validateImageFile(file) {
    const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
    const maxSize = 5 * 1024 * 1024; // 5MB

    if (!allowedTypes.includes(file.type)) {
      console.error('Invalid file type:', file.type);
      return false;
    }

    if (file.size > maxSize) {
      console.error('File too large:', file.size);
      return false;
    }

    return true;
  }
}

// Usage example
async function handleProductCreation(formData, imageFiles) {
  const productManager = new ProductManager('your_partner_token');
  
  const productData = {
    name: formData.get('name'),
    description: formData.get('description'),
    price: parseFloat(formData.get('price')),
    currency: formData.get('currency') || 'USD',
    sku: formData.get('sku'),
    barcode: formData.get('barcode'),
    metadata: {
      weight: formData.get('weight'),
      dimensions: formData.get('dimensions'),
      category: formData.get('category')
    },
    is_active: formData.get('is_active') === 'true'
  };

  const result = await productManager.createProduct(productData, imageFiles);
  
  if (result.success) {
    showSuccessMessage(`Product created! PIIC generated: ${result.piicSummary}`);
    refreshProductList();
  } else {
    showErrorMessage(result.error, result.details);
  }
}

Pattern 2: Intelligent Product Listing with Filtering

Implement product listing with advanced filtering and pagination for campaign integration.

class ProductListManager {
  constructor(partnerToken) {
    this.partnerToken = partnerToken;
    this.headers = {
      'Authorization': `Bearer ${partnerToken}`,
      'Content-Type': 'application/json'
    };
  }

  async getProducts(filters = {}) {
    const queryParams = new URLSearchParams();
    
    // Add pagination
    queryParams.append('page', filters.page || 1);
    queryParams.append('limit', filters.limit || 20);
    
    // Add filters
    if (filters.is_active !== undefined) {
      queryParams.append('is_active', filters.is_active);
    }
    
    if (filters.category_id) {
      queryParams.append('category_id', filters.category_id);
    }

    try {
      const response = await fetch(`/partner/v1/products/list?${queryParams.toString()}`, {
        method: 'GET',
        headers: this.headers
      });

      const result = await response.json();

      if (result.success) {
        return {
          success: true,
          products: result.data.products,
          pagination: result.data.pagination,
          totalProducts: result.data.pagination.total
        };
      }

      return { success: false, error: result.error };
    } catch (error) {
      console.error('Failed to fetch products:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async getProductsForCampaigns() {
    // Get only active products suitable for campaigns
    return await this.getProducts({ 
      is_active: true,
      limit: 100 // Get more products for campaign selection
    });
  }

  async searchProducts(searchTerm, filters = {}) {
    const result = await this.getProducts(filters);
    
    if (result.success && searchTerm) {
      const filteredProducts = result.products.filter(product => 
        product.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
        product.description?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        product.sku?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        product.piic_summary?.toLowerCase().includes(searchTerm.toLowerCase())
      );

      return {
        ...result,
        products: filteredProducts,
        searchTerm
      };
    }

    return result;
  }
}

// Implementation example
async function buildProductSelector() {
  const listManager = new ProductListManager('your_partner_token');
  
  const result = await listManager.getProductsForCampaigns();
  
  if (result.success) {
    const productSelect = document.getElementById('campaign-products');
    productSelect.innerHTML = '';

    result.products.forEach(product => {
      const option = document.createElement('option');
      option.value = product.id;
      option.textContent = `${product.name} - $${product.price} (${product.sku || 'No SKU'})`;
      option.dataset.piicSummary = product.piic_summary;
      productSelect.appendChild(option);
    });

    // Show PIIC summary on selection
    productSelect.addEventListener('change', (e) => {
      const selectedOption = e.target.selectedOptions[0];
      if (selectedOption) {
        document.getElementById('product-piic-summary').textContent = 
          selectedOption.dataset.piicSummary || 'No PIIC summary available';
      }
    });
  }
}

Pattern 3: Dynamic Product Updates with Image Management

Handle product updates including image additions and removals.

class ProductUpdateManager {
  constructor(partnerToken) {
    this.partnerToken = partnerToken;
    this.headers = {
      'Authorization': `Bearer ${partnerToken}`
    };
  }

  async updateProduct(productId, updates, newImages = [], removeImages = []) {
    const formData = new FormData();
    
    // Add product ID
    formData.append('id', productId);
    
    // Add update fields
    Object.keys(updates).forEach(key => {
      if (updates[key] !== undefined) {
        if (typeof updates[key] === 'object') {
          formData.append(key, JSON.stringify(updates[key]));
        } else {
          formData.append(key, updates[key]);
        }
      }
    });

    // Add images to remove
    if (removeImages.length > 0) {
      formData.append('remove_images', JSON.stringify(removeImages));
    }

    // Add new images
    newImages.forEach(file => {
      formData.append('images[]', file);
    });

    try {
      const response = await fetch('/partner/v1/products/update', {
        method: 'PUT',
        headers: this.headers,
        body: formData
      });

      const result = await response.json();

      if (result.success) {
        return {
          success: true,
          product: result.data,
          updatedPiicSummary: result.data.piic_summary
        };
      }

      return { success: false, error: result.error };
    } catch (error) {
      console.error('Product update failed:', error);
      return { success: false, error: 'Network error during update' };
    }
  }

  async toggleProductStatus(productId, isActive) {
    return await this.updateProduct(productId, { is_active: isActive });
  }

  async updateProductPricing(productId, price, currency = 'USD') {
    return await this.updateProduct(productId, { price, currency });
  }

  async bulkUpdateProducts(updates) {
    const results = [];
    
    for (const update of updates) {
      const result = await this.updateProduct(
        update.id, 
        update.data, 
        update.newImages, 
        update.removeImages
      );
      results.push({ id: update.id, ...result });
    }

    return results;
  }
}

// Usage example
async function handleProductImageUpdate(productId, newFiles, imagesToRemove) {
  const updateManager = new ProductUpdateManager('your_partner_token');
  
  const result = await updateManager.updateProduct(
    productId, 
    {}, // No other updates, just images
    newFiles, 
    imagesToRemove
  );

  if (result.success) {
    showSuccessMessage('Product images updated successfully!');
    refreshProductDisplay(result.product);
    
    // Show updated PIIC summary if it changed
    if (result.updatedPiicSummary) {
      displayPiicSummary(result.updatedPiicSummary);
    }
  } else {
    showErrorMessage(result.error);
  }
}

Pattern 4: Category Management and Product Association

Implement comprehensive category management with product relationships.

class CategoryManager {
  constructor(partnerToken) {
    this.partnerToken = partnerToken;
    this.headers = {
      'Authorization': `Bearer ${partnerToken}`,
      'Content-Type': 'application/json'
    };
  }

  async createCategory(name) {
    try {
      const response = await fetch('/partner/v1/category/new', {
        method: 'POST',
        headers: this.headers,
        body: JSON.stringify({ name })
      });

      const result = await response.json();

      if (result.success) {
        return {
          success: true,
          category: result.data,
          piicSummary: result.data.piic_summary
        };
      }

      return { success: false, error: result.error };
    } catch (error) {
      console.error('Category creation failed:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async getCategories(includeProducts = false) {
    const queryParams = includeProducts ? '?include_products=true' : '';
    
    try {
      const response = await fetch(`/partner/v1/category/list${queryParams}`, {
        method: 'GET',
        headers: this.headers
      });

      const result = await response.json();

      if (result.success) {
        return {
          success: true,
          categories: result.data.categories
        };
      }

      return { success: false, error: result.error };
    } catch (error) {
      console.error('Failed to fetch categories:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async assignProductToCategory(productId, categoryId) {
    try {
      const response = await fetch('/partner/v1/products/assign-category', {
        method: 'POST',
        headers: this.headers,
        body: JSON.stringify({
          product_id: productId,
          category_id: categoryId
        })
      });

      const result = await response.json();
      return { success: result.success, message: result.message, error: result.error };
    } catch (error) {
      console.error('Product assignment failed:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async removeProductFromCategory(productId, categoryId) {
    try {
      const response = await fetch('/partner/v1/products/remove-category', {
        method: 'DELETE',
        headers: this.headers,
        body: JSON.stringify({
          product_id: productId,
          category_id: categoryId
        })
      });

      const result = await response.json();
      return { success: result.success, message: result.message, error: result.error };
    } catch (error) {
      console.error('Product removal failed:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async getCategoryProducts(categoryId) {
    try {
      const response = await fetch(`/partner/v1/category/${categoryId}/products`, {
        method: 'GET',
        headers: this.headers
      });

      const result = await response.json();

      if (result.success) {
        return {
          success: true,
          category: result.data.category,
          products: result.data.products
        };
      }

      return { success: false, error: result.error };
    } catch (error) {
      console.error('Failed to fetch category products:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async deleteCategory(categoryId, reassignProducts = false) {
    if (reassignProducts) {
      // First check for associated products
      const categoryData = await this.getCategoryProducts(categoryId);
      
      if (categoryData.success && categoryData.products.length > 0) {
        throw new Error('Category has associated products. Reassign them first.');
      }
    }

    try {
      const response = await fetch('/partner/v1/category/delete', {
        method: 'DELETE',
        headers: this.headers,
        body: JSON.stringify({ id: categoryId })
      });

      const result = await response.json();
      return { success: result.success, message: result.message, error: result.error };
    } catch (error) {
      console.error('Category deletion failed:', error);
      return { success: false, error: 'Network error' };
    }
  }
}

// Implementation example
async function buildCategoryProductManager() {
  const categoryManager = new CategoryManager('your_partner_token');
  
  // Load categories and products
  const [categoriesResult, productsResult] = await Promise.all([
    categoryManager.getCategories(true),
    new ProductListManager('your_partner_token').getProducts()
  ]);

  if (categoriesResult.success && productsResult.success) {
    renderCategoryProductInterface(categoriesResult.categories, productsResult.products);
  }
}

async function handleProductCategoryAssignment(productId, categoryId) {
  const categoryManager = new CategoryManager('your_partner_token');
  
  const result = await categoryManager.assignProductToCategory(productId, categoryId);
  
  if (result.success) {
    showSuccessMessage('Product assigned to category successfully!');
    refreshCategoryView();
  } else {
    showErrorMessage(result.error);
  }
}

Complete Product Management Integration

Unified Product Management System

class CashInProductSystem {
  constructor(partnerToken) {
    this.partnerToken = partnerToken;
    this.productManager = new ProductManager(partnerToken);
    this.listManager = new ProductListManager(partnerToken);
    this.updateManager = new ProductUpdateManager(partnerToken);
    this.categoryManager = new CategoryManager(partnerToken);
  }

  async initializeProductSystem() {
    try {
      const [categories, products] = await Promise.all([
        this.categoryManager.getCategories(true),
        this.listManager.getProducts({ limit: 50 })
      ]);

      return {
        success: true,
        categories: categories.categories || [],
        products: products.products || [],
        pagination: products.pagination
      };
    } catch (error) {
      console.error('Failed to initialize product system:', error);
      return { success: false, error: 'System initialization failed' };
    }
  }

  async createProductWithCategory(productData, imageFiles, categoryName) {
    // Create category if it doesn't exist
    let categoryId;
    const categories = await this.categoryManager.getCategories();
    
    if (categories.success) {
      const existingCategory = categories.categories.find(cat => 
        cat.name.toLowerCase() === categoryName.toLowerCase()
      );
      
      if (existingCategory) {
        categoryId = existingCategory.id;
      } else {
        const newCategory = await this.categoryManager.createCategory(categoryName);
        if (newCategory.success) {
          categoryId = newCategory.category.id;
        }
      }
    }

    // Create product
    const productResult = await this.productManager.createProduct(productData, imageFiles);
    
    if (productResult.success && categoryId) {
      // Assign to category
      await this.categoryManager.assignProductToCategory(
        productResult.product.id, 
        categoryId
      );
      
      return {
        ...productResult,
        categoryAssigned: true,
        categoryId
      };
    }

    return productResult;
  }

  async getProductsForCampaign(campaignType = 'viral') {
    const products = await this.listManager.getProducts({ is_active: true });
    
    if (!products.success) return products;

    // Filter products based on viral potential (using PIIC summaries)
    const campaignReadyProducts = products.products.filter(product => {
      // Products with good PIIC summaries are better for campaigns
      return product.piic_summary && product.piic_summary.length > 50;
    });

    return {
      success: true,
      products: campaignReadyProducts,
      totalCount: campaignReadyProducts.length
    };
  }

  async bulkUpdateProductStatus(productIds, isActive) {
    const updates = productIds.map(id => ({
      id,
      data: { is_active: isActive }
    }));

    return await this.updateManager.bulkUpdateProducts(updates);
  }

  async deleteProductSafely(productId) {
    try {
      const response = await fetch('/products/delete', {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${this.partnerToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ id: productId })
      });

      const result = await response.json();
      return { success: result.success, message: result.message, error: result.error };
    } catch (error) {
      console.error('Product deletion failed:', error);
      return { success: false, error: 'Network error during deletion' };
    }
  }

  async generateProductInsights() {
    const products = await this.listManager.getProducts({ limit: 100 });
    
    if (!products.success) return products;

    const insights = {
      totalProducts: products.products.length,
      activeProducts: products.products.filter(p => p.is_active).length,
      averagePrice: products.products.reduce((sum, p) => sum + (p.price || 0), 0) / products.products.length,
      productsWithImages: products.products.filter(p => p.image_url && p.image_url.length > 0).length,
      productsWithPiicSummaries: products.products.filter(p => p.piic_summary).length,
      priceRanges: this.calculatePriceRanges(products.products),
      topCategories: await this.getTopCategories()
    };

    return { success: true, insights };
  }

  calculatePriceRanges(products) {
    const ranges = {
      under25: 0,
      range25to50: 0,
      range50to100: 0,
      over100: 0
    };

    products.forEach(product => {
      const price = product.price || 0;
      if (price < 25) ranges.under25++;
      else if (price < 50) ranges.range25to50++;
      else if (price < 100) ranges.range50to100++;
      else ranges.over100++;
    });

    return ranges;
  }

  async getTopCategories() {
    const categories = await this.categoryManager.getCategories(true);
    
    if (!categories.success) return [];

    return categories.categories
      .map(cat => ({
        name: cat.name,
        productCount: cat.product_count || 0,
        piicSummary: cat.piic_summary
      }))
      .sort((a, b) => b.productCount - a.productCount)
      .slice(0, 5);
  }
}

Error Handling and Best Practices

Comprehensive Error Management

class ProductErrorHandler {
  static handleProductError(error, context = {}) {
    console.error('Product management error:', error, context);
    
    switch (error.type || error.error) {
      case 'Validation failed':
        return {
          userMessage: ProductErrorHandler.formatValidationErrors(error.details),
          retry: false
        };
        
      case 'File too large':
        return {
          userMessage: `Image file too large. Maximum size: ${error.details?.max_file_size || '5MB'}`,
          retry: false
        };
        
      case 'Product with this SKU already exists':
        return {
          userMessage: 'A product with this SKU already exists. Please use a different SKU.',
          retry: false
        };
        
      case 'Cannot delete category: has associated products':
        return {
          userMessage: `Cannot delete category: ${error.details?.associated_products_count || 'some'} products are still assigned. Please reassign them first.`,
          retry: false,
          action: 'reassign_products'
        };
        
      default:
        return {
          userMessage: 'An unexpected error occurred. Please try again.',
          retry: true
        };
    }
  }

  static formatValidationErrors(details) {
    if (!details) return 'Validation failed';
    
    return Object.entries(details)
      .map(([field, message]) => `${field}: ${message}`)
      .join(', ');
  }
}

Testing Your Integration

Product Management Tests

async function testProductManagement() {
  const productSystem = new CashInProductSystem('sk_test_your_test_key');
  
  console.log('Testing product system initialization...');
  const initResult = await productSystem.initializeProductSystem();
  console.log('Init result:', initResult);
  
  console.log('Testing product creation...');
  const createResult = await productSystem.createProductWithCategory({
    name: 'Test Product',
    description: 'A test product for viral campaigns',
    price: 29.99,
    sku: 'TEST-001'
  }, [], 'Test Category');
  console.log('Create result:', createResult);
  
  console.log('Testing campaign-ready products...');
  const campaignProducts = await productSystem.getProductsForCampaign();
  console.log('Campaign products:', campaignProducts);
  
  console.log('Testing product insights...');
  const insights = await productSystem.generateProductInsights();
  console.log('Insights:', insights);
}

// Run tests in development
if (process.env.NODE_ENV === 'development') {
  window.testProducts = testProductManagement;
}

This comprehensive integration guide provides all the tools needed to implement CashIn's Product Management system, enabling you to transform your inventory into intelligent, campaign-ready assets that drive viral growth through the PIIC framework.

Product Endpoint Details

Create Product (POST /partner/v1/products/new)

  • Use Case: Add new products to viral marketing ecosystem

  • Content Type: multipart/form-data (for image uploads)

  • Required Fields: name

  • Optional Fields: description, price, currency, sku, barcode, metadata, is_active

  • File Upload: images[] - Max 10 files, 5MB each, formats: JPG, PNG, WebP

  • Returns: Complete product object with PIIC-generated summary

  • PIIC Integration: Auto-generates intelligent summary for campaign readiness

List Products (GET /partner/v1/products/list)

  • Use Case: Browse products for campaign creation, inventory management

  • Query Parameters:

    • page (default: 1)

    • limit (default: 20, max: 100)

    • is_active (true/false filter)

    • category_id (UUID filter)

  • Returns: Paginated product list with category associations

  • Campaign Integration: Filter for campaign-ready products

  • Analytics: Includes product counts and pagination metadata

Update Product (PUT /partner/v1/products/update)

  • Use Case: Modify product details, manage images, update pricing

  • Content Type: multipart/form-data (for image operations)

  • Required Fields: id

  • Optional Fields: All product fields, remove_images array

  • Image Management: Add new images via images[], remove via remove_images

  • Returns: Updated product with regenerated PIIC summary

  • Intelligence: Auto-updates PIIC summary when content changes

Delete Product (DELETE /partner/v1/products/delete)

  • Use Case: Remove discontinued products

  • Required Fields: id

  • Returns: Success confirmation

  • Safety Features:

    • Prevents deletion if associated with active campaigns

    • Removes all category associations automatically

  • Error Handling: Returns conflict error if product is campaign-linked

Assign Product to Category (POST /partner/v1/products/assign-category)

  • Use Case: Organize products for targeted campaigns

  • Required Fields: product_id, category_id

  • Returns: Success confirmation

  • Validation: Ensures both product and category exist and belong to partner

  • Campaign Ready: Enables category-based campaign targeting

  • Conflict Prevention: Prevents duplicate assignments

Remove Product from Category (DELETE /partner/v1/products/remove-category)

  • Use Case: Reorganize product categorization

  • Required Fields: product_id, category_id

  • Returns: Success confirmation

  • Validation: Ensures assignment exists before removal

  • Flexibility: Products can belong to multiple categories

Category Endpoint Details

Create Category (POST /partner/v1/category/new)

  • Use Case: Organize products for viral campaigns and targeting

  • Required Fields: name

  • Returns: Category with PIIC-generated summary

  • Intelligence: Auto-generates category insights based on name

  • Campaign Integration: Enables category-based reward targeting

  • Uniqueness: Prevents duplicate category names per partner

List Categories (GET /partner/v1/category/list)

  • Use Case: Browse organization structure, campaign planning

  • Query Parameters: include_products (true/false, default: false)

  • Returns: Categories with product counts and optional product details

  • Performance: Lightweight by default, detailed when requested

  • Analytics: Includes product_count for each category

Update Category (PUT /partner/v1/category/update)

  • Use Case: Rename categories, update organization

  • Required Fields: id, name

  • Returns: Updated category with regenerated PIIC summary

  • Intelligence: PIIC summary updates based on new name and associated products

  • Validation: Prevents duplicate category names

Delete Category (DELETE /partner/v1/category/delete)

  • Use Case: Remove unused categories

  • Required Fields: id

  • Returns: Success confirmation or detailed error

  • Safety Features: Requires all products to be reassigned first

  • Error Details: Lists associated products if deletion fails

  • Data Protection: Prevents accidental data loss

Get Category Products (GET /partner/v1/category/{category_id}/products)

  • Use Case: View products in specific category for campaign creation

  • URL Parameter: category_id (UUID)

  • Returns: Category details with all associated products

  • Campaign Ready: Perfect for category-specific promotional campaigns

  • Organization: Clear view of product groupings