Skip to main content
The Custom Webshop integration connects your custom e-commerce platform with Totebot so your AI agent can search your products, manage carts, and help customers make purchases.
Custom Webshop integrations are managed by our support team. To set one up for your store, contact us at support@totebot.ai with a brief description of your store and your product catalog format. We’ll configure the connection, run validation, and enable the integration for your agent.
The rest of this page documents the technical contract — the shape of the data your store needs to expose. Sharing this information ahead of your support request helps us configure the integration faster.

Prerequisites

  • A publicly reachable HTTPS endpoint (or product feed) that exposes your catalog. Our team will work with you on the exact format during setup — JSON, CSV, and XML feeds are all supported.

What our team will set up

Once connected, your store will have three logical endpoints powering the integration:
  • Products endpoint: paginated list of your full catalog.
  • Search endpoint: text + filter search across your catalog.
  • Get-by-IDs endpoint: bulk fetch of specific products.
These are managed for you. The sections below describe the shape of each one for reference.

Products API

Your API must return products with pagination. Endpoint:
GET {productsUrl}?page=1&limit=20
Query Parameters:
  • page: 1-based page number (default: 1)
  • limit: page size (default: 20, recommended up to 250)
Response Format:
{
  "products": [
    {
      "id": "123",
      "name": "Product name",
      "description": "Optional product description",
      "url": "https://shop.example.com/products/handle",
      "productType": "Shoes",
      "tags": ["summer", "running"],
      "status": "active",
      "availability": true,
      "price": { 
        "amount": 29.99, 
        "currency": "USD" 
      },
      "images": [
        { 
          "url": "https://example.com/image.jpg", 
          "altText": "Product image" 
        }
      ],
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-02T00:00:00.000Z",
      "attributes": {
        "color": "red",
        "size": "10"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1000,
    "hasNextPage": true
  }
}
Field Requirements:
  • Required: id, name, url, status (one of active|archived|draft), availability (boolean)
  • Recommended: description, productType, tags, price.amount, price.currency, images[], created_at, updated_at
  • Optional: attributes (key-value pairs for custom fields)

Search API

Your search API should handle product queries with optional filters. Endpoint:
GET {searchUrl}?query=running shoes&filters[availability]=true&filters[price][min]=10&filters[price][max]=100&filters[productType]=Shoes&page=1&limit=10
Query Parameters:
  • query: Search query string (required)
  • filters[availability]: Filter by availability (boolean)
  • filters[price][min]: Minimum price filter (number)
  • filters[price][max]: Maximum price filter (number)
  • filters[productType]: Filter by product type (string)
  • page: 1-based page number (default: 1)
  • limit: page size (default: 10)
Response Format:
{
  "products": [
    {
      "id": "123",
      "name": "Running Shoes",
      "description": "High-performance running shoes",
      "url": "https://shop.example.com/products/running-shoes",
      "productType": "Shoes",
      "tags": ["running", "athletic"],
      "status": "active",
      "availability": true,
      "price": { 
        "amount": 99.99, 
        "currency": "USD" 
      },
      "images": [
        { 
          "url": "https://example.com/running-shoes.jpg", 
          "altText": "Running shoes" 
        }
      ],
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-02T00:00:00.000Z",
      "attributes": {
        "color": "blue",
        "size": "10"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 25,
    "hasNextPage": true
  },
  "query": "running shoes"
}

Get by IDs API

Your get by IDs API should fetch specific products by their IDs. Endpoint:
GET {getByIdsUrl}?ids=123,456,789
Query Parameters:
  • ids: Comma-separated list of product IDs (required)
Response Format:
{
  "products": [
    {
      "id": "123",
      "name": "Product 1",
      "description": "Description for product 1",
      "url": "https://shop.example.com/products/product-1",
      "productType": "Shoes",
      "tags": ["summer"],
      "status": "active",
      "availability": true,
      "price": { 
        "amount": 49.99, 
        "currency": "USD" 
      },
      "images": [
        { 
          "url": "https://example.com/product1.jpg", 
          "altText": "Product 1" 
        }
      ],
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-02T00:00:00.000Z",
      "attributes": {
        "color": "red"
      }
    },
    {
      "id": "456",
      "name": "Product 2",
      "description": "Description for product 2",
      "url": "https://shop.example.com/products/product-2",
      "productType": "Clothing",
      "tags": ["winter"],
      "status": "active",
      "availability": false,
      "price": { 
        "amount": 79.99, 
        "currency": "USD" 
      },
      "images": [
        { 
          "url": "https://example.com/product2.jpg", 
          "altText": "Product 2" 
        }
      ],
      "created_at": "2024-01-01T00:00:00.000Z",
      "updated_at": "2024-01-02T00:00:00.000Z",
      "attributes": {
        "size": "M"
      }
    }
  ]
}
Note: If no products are found or if the ids parameter is empty, return an empty products array:
{
  "products": []
}

Reference Implementation

Here’s a complete reference implementation based on ToteBot’s own products API: Products Controller (Node.js/Express):
// GET /products?page=1&limit=20
app.get('/products', async (req, res) => {
  const { page = 1, limit = 20 } = req.query;
  
  try {
    const products = await getProductsFromDatabase(page, limit);
    const total = await getTotalProductCount();
    
    res.json({
      products: products.map(product => ({
        id: product.id,
        name: product.name,
        description: product.description,
        url: product.url,
        productType: product.productType,
        tags: product.tags,
        status: product.status,
        availability: product.availability,
        price: product.price ? {
          amount: product.price.amount,
          currency: product.price.currency
        } : undefined,
        images: product.images?.map(img => ({
          url: img.url,
          altText: img.altText
        })),
        created_at: product.created_at,
        updated_at: product.updated_at,
        attributes: product.attributes || {}
      })),
      pagination: {
        page: parseInt(page),
        limit: parseInt(limit),
        total,
        hasNextPage: (page * limit) < total
      }
    });
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
Search Controller:
// GET /products/search?query=shoes&filters[availability]=true&page=1&limit=10
app.get('/products/search', async (req, res) => {
  const { query, filters = {}, page = 1, limit = 10 } = req.query;
  
  if (!query) {
    return res.status(400).json({ error: 'Query parameter is required' });
  }
  
  try {
    const searchResults = await searchProducts(query, filters, page, limit);
    
    res.json({
      products: searchResults.products.map(product => ({
        // Same product mapping as above
        id: product.id,
        name: product.name,
        // ... other fields
      })),
      pagination: {
        page: parseInt(page),
        limit: parseInt(limit),
        total: searchResults.total,
        hasNextPage: (page * limit) < searchResults.total
      },
      query
    });
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
Get by IDs Controller:
// GET /products/ids?ids=123,456,789
app.get('/products/ids', async (req, res) => {
  const { ids } = req.query;
  
  if (!ids) {
    return res.json({ products: [] });
  }
  
  const idArray = ids.split(',').map(id => id.trim()).filter(Boolean);
  
  if (idArray.length === 0) {
    return res.json({ products: [] });
  }
  
  try {
    const products = await getProductsByIds(idArray);
    
    res.json({
      products: products.map(product => ({
        // Same product mapping as above
        id: product.id,
        name: product.name,
        // ... other fields
      }))
    });
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});

API Validation

ToteBot will validate your API endpoints during the integration setup process:
  1. Products API: Tests connectivity and response structure
  2. Search API: Validates search functionality with a test query
  3. Get by IDs API: Ensures proper handling of product ID lookups
All endpoints must return valid JSON responses with the expected structure. If any validation fails, you’ll see specific error messages to help you fix the issues.

Error Handling

Your APIs should handle errors gracefully and return appropriate HTTP status codes: Common Error Responses:
{
  "error": "Product not found",
  "message": "The requested product ID does not exist",
  "code": "PRODUCT_NOT_FOUND"
}
HTTP Status Codes:
  • 200: Success
  • 400: Bad Request (invalid parameters)
  • 404: Not Found (product/endpoint not found)
  • 500: Internal Server Error

Security Considerations

  • HTTPS Required: All API endpoints must use HTTPS
  • Rate Limiting: Implement rate limiting to prevent abuse
  • Input Validation: Validate all query parameters and request data
  • CORS: Configure CORS headers if needed for web requests
  • Authentication: Consider adding API keys for production use

Performance Optimization

  • Caching: Implement caching for frequently accessed product data
  • Pagination: Use efficient pagination to handle large product catalogs
  • Database Indexing: Ensure proper database indexes for search queries
  • CDN: Use a CDN for product images to improve loading times

Best Practices

Data Consistency

  • Ensure product availability is real-time
  • Keep inventory updated across all channels
  • Handle price changes during checkout flow
  • Validate product data before returning responses

Testing Your Implementation

  1. API Endpoints: Test all three endpoints with various parameters
  2. Error Scenarios: Test with invalid IDs, empty responses, network errors
  3. Performance: Load test with concurrent users and large catalogs
  4. Integration: Test end-to-end with ToteBot’s conversation flow
  5. Edge Cases: Test empty product lists, malformed JSON, timeout scenarios

Validation Rules

  • Products API: Must return products array and pagination object with hasNextPage boolean
  • Search API: Must return products array, pagination object, and query string
  • Get by IDs API: Must return products array (can be empty if no products found)
  • Product Objects: Should align with the field requirements above; unknown fields go into attributes

Troubleshooting

  • Zero Products: Verify your API returns products[] and pagination.hasNextPage
  • Search Issues: Ensure your search endpoint handles queries and filters correctly
  • Connectivity: Ensure all URLs are reachable from ToteBot’s backend
  • Validation Errors: Check that your API responses match the expected JSON structure
  • Performance: Monitor response times and implement caching if needed

Common Issues

Issue: “Unable to connect to Products API”
  • Solution: Verify the URL is correct and accessible via HTTPS
Issue: “Invalid response structure from Search API”
  • Solution: Ensure your search endpoint returns the expected JSON format with products and pagination
Issue: “Get by IDs API connectivity test failed”
  • Solution: Check that your endpoint handles empty ids parameter and returns {"products": []}
Issue: Products not updating properly
  • Solution: Verify your pagination logic and ensure hasNextPage is calculated correctly