> ## Documentation Index
> Fetch the complete documentation index at: https://docs.totebot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Webshop Integration

> Custom webshop integration for Totebot, managed by our team.

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.

<Note>Custom Webshop is available on **Pro** and **Scale** plans.</Note>

<Note>
  Custom Webshop integrations are **managed by our support team**. To set one up for your store, contact us at [support@totebot.ai](mailto:support@totebot.ai?subject=Custom%20Webshop%20integration) 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.
</Note>

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:**

```http theme={null}
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:**

```json theme={null}
{
  "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:**

```http theme={null}
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:**

```json theme={null}
{
  "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:**

```http theme={null}
GET {getByIdsUrl}?ids=123,456,789
```

**Query Parameters:**

* `ids`: Comma-separated list of product IDs (required)

**Response Format:**

```json theme={null}
{
  "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:

```json theme={null}
{
  "products": []
}
```

### Reference Implementation

Here's a complete reference implementation based on ToteBot's own products API:

**Products Controller (Node.js/Express):**

```javascript theme={null}
// 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:**

```javascript theme={null}
// 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:**

```javascript theme={null}
// 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:**

```json theme={null}
{
  "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
