Products Block

Product catalog and inventory management API
The Products Block provides complete e-commerce product management. Create product catalogs, manage inventory, handle variants, organize into categories, and deliver product data to any frontend.
Features
- Product Catalog - Rich product data with unlimited attributes
- Inventory Management - Real-time stock tracking with alerts
- Product Variants - Size, color, and custom variant support
- Categories & Collections - Hierarchical organization
- Dynamic Pricing - Multi-currency and tiered pricing
- Product Search - Full-text search with filtering
Quick Start
npm install @23blocks/sdk
API Endpoint
| Service | URL |
|---|---|
| Products | https://products.api.us.23blocks.com |
Environment Routing: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { products: 'https://products.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});
// Create a product
const product = await client.products.create({
name: 'Premium Wireless Headphones',
sku: 'WH-PRO-2024',
price: { amount: 29900, currency: 'USD' },
inventory: { quantity: 100, track_stock: true }
});
// List products with filters
const products = await client.products.list({
category: 'electronics',
in_stock: true,
sort: '-created_at'
});
Authentication
All API requests require authentication headers:
curl -X GET "https://api.23blocks.com/v1/products" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-App-Id: YOUR_APP_ID" \
-H "Content-Type: application/json"
Products
Create Product
Create a new product in your catalog.
POST /v1/products
Request Body:
{
"data": {
"type": "products",
"attributes": {
"name": "Premium Wireless Headphones",
"sku": "WH-PRO-2024",
"description": "High-fidelity audio with active noise cancellation and 30-hour battery life.",
"short_description": "Premium wireless headphones with ANC",
"status": "active",
"type": "physical",
"price": {
"amount": 29900,
"currency": "USD",
"compare_at": 34900
},
"cost": {
"amount": 12000,
"currency": "USD"
},
"inventory": {
"quantity": 247,
"track_stock": true,
"allow_backorder": false,
"low_stock_threshold": 25
},
"weight": {
"value": 250,
"unit": "g"
},
"dimensions": {
"length": 20,
"width": 18,
"height": 8,
"unit": "cm"
},
"attributes": {
"brand": "AudioPro",
"battery_life": "30 hours",
"connectivity": "Bluetooth 5.3",
"driver_size": "40mm"
},
"seo": {
"title": "Premium Wireless Headphones | AudioPro",
"description": "Experience studio-quality sound with our premium wireless headphones.",
"slug": "premium-wireless-headphones"
},
"metadata": {
"warehouse_location": "A1-23"
}
},
"relationships": {
"categories": {
"data": [
{ "type": "categories", "id": "cat_electronics" },
{ "type": "categories", "id": "cat_audio" }
]
}
}
}
}
Response:
{
"data": {
"type": "products",
"id": "prod_abc123",
"attributes": {
"name": "Premium Wireless Headphones",
"sku": "WH-PRO-2024",
"description": "High-fidelity audio with active noise cancellation...",
"status": "active",
"type": "physical",
"price": {
"amount": 29900,
"currency": "USD",
"compare_at": 34900,
"formatted": "$299.00",
"compare_at_formatted": "$349.00"
},
"inventory": {
"quantity": 247,
"available": 247,
"reserved": 0,
"in_stock": true,
"track_stock": true,
"low_stock": false
},
"created_at": "2026-01-02T10:30:00Z",
"updated_at": "2026-01-02T10:30:00Z"
}
}
}
List Products
Retrieve a paginated list of products with filtering and sorting.
GET /v1/products
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 20, max: 100) |
filter[status] | string | Filter by status: active, draft, archived |
filter[category] | string | Filter by category ID or slug |
filter[in_stock] | boolean | Filter by stock availability |
filter[type] | string | Filter by type: physical, digital, service |
filter[price_min] | integer | Minimum price in cents |
filter[price_max] | integer | Maximum price in cents |
filter[created_after] | datetime | Products created after date |
search | string | Full-text search query |
sort | string | Sort field: name, price, created_at, -created_at |
include | string | Include related resources: variants, categories, images |
Example:
GET /v1/products?filter[category]=electronics&filter[in_stock]=true&sort=-created_at&include=variants,images
Get Product
Retrieve a single product by ID or SKU.
GET /v1/products/:id
GET /v1/products/sku/:sku
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: variants, categories, images, related |
Update Product
Update an existing product.
PATCH /v1/products/:id
Request Body:
{
"data": {
"type": "products",
"id": "prod_abc123",
"attributes": {
"price": {
"amount": 27900,
"compare_at": 29900
},
"status": "active"
}
}
}
Delete Product
Delete a product (soft delete by default).
DELETE /v1/products/:id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
permanent | boolean | Permanently delete (default: false) |
Product Variants
Variants represent different versions of a product (size, color, material).
Create Variant
POST /v1/products/:product_id/variants
Request Body:
{
"data": {
"type": "variants",
"attributes": {
"name": "Black",
"sku": "WH-PRO-2024-BLK",
"options": {
"color": "black"
},
"price_modifier": {
"type": "fixed",
"amount": 0
},
"inventory": {
"quantity": 120,
"track_stock": true
},
"weight": {
"value": 250,
"unit": "g"
},
"position": 1
}
}
}
Response:
{
"data": {
"type": "variants",
"id": "var_xyz789",
"attributes": {
"name": "Black",
"sku": "WH-PRO-2024-BLK",
"options": {
"color": "black"
},
"price": {
"amount": 29900,
"currency": "USD",
"formatted": "$299.00"
},
"inventory": {
"quantity": 120,
"in_stock": true
},
"position": 1,
"created_at": "2026-01-02T10:35:00Z"
}
}
}
List Variants
GET /v1/products/:product_id/variants
Update Variant
PATCH /v1/products/:product_id/variants/:variant_id
Delete Variant
DELETE /v1/products/:product_id/variants/:variant_id
Variant Options
Define available options for variants:
PUT /v1/products/:product_id/options
Request Body:
{
"data": {
"options": [
{
"name": "Color",
"values": ["Black", "White", "Navy Blue"]
},
{
"name": "Size",
"values": ["S", "M", "L", "XL"]
}
]
}
}
Categories
Organize products into hierarchical categories.
Create Category
POST /v1/categories
Request Body:
{
"data": {
"type": "categories",
"attributes": {
"name": "Electronics",
"slug": "electronics",
"description": "Electronic devices and accessories",
"parent_id": null,
"position": 1,
"seo": {
"title": "Electronics - Shop the Latest Tech",
"description": "Browse our selection of electronics and gadgets."
},
"metadata": {
"icon": "electronics-icon"
}
}
}
}
List Categories
GET /v1/categories
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[parent_id] | string | Filter by parent (null for root) |
include | string | Include: children, products, product_count |
tree | boolean | Return full category tree |
Get Category
GET /v1/categories/:id
GET /v1/categories/slug/:slug
Update Category
PATCH /v1/categories/:id
Delete Category
DELETE /v1/categories/:id
Assign Products to Category
POST /v1/categories/:id/products
Request Body:
{
"data": {
"product_ids": ["prod_abc123", "prod_def456"]
}
}
Collections
Curated product groupings for merchandising.
Create Collection
POST /v1/collections
Request Body:
{
"data": {
"type": "collections",
"attributes": {
"name": "Summer Sale 2026",
"slug": "summer-sale-2026",
"description": "Hot deals for summer",
"type": "manual",
"status": "active",
"start_date": "2026-06-01T00:00:00Z",
"end_date": "2026-08-31T23:59:59Z",
"rules": null,
"seo": {
"title": "Summer Sale - Up to 50% Off",
"description": "Shop our biggest summer sale with discounts up to 50%."
}
}
}
}
Automated Collections
Create collections with automatic product matching:
{
"data": {
"type": "collections",
"attributes": {
"name": "New Arrivals",
"type": "automated",
"rules": {
"match": "all",
"conditions": [
{
"field": "created_at",
"operator": "greater_than",
"value": "30_days_ago"
},
{
"field": "status",
"operator": "equals",
"value": "active"
}
]
}
}
}
}
List Collections
GET /v1/collections
Get Collection Products
GET /v1/collections/:id/products
Add Products to Collection
POST /v1/collections/:id/products
Remove Products from Collection
DELETE /v1/collections/:id/products/:product_id
Inventory
Get Inventory Levels
GET /v1/products/:id/inventory
Response:
{
"data": {
"type": "inventory",
"id": "inv_abc123",
"attributes": {
"product_id": "prod_abc123",
"total_quantity": 247,
"available": 235,
"reserved": 12,
"committed": 0,
"in_stock": true,
"low_stock": false,
"locations": [
{
"location_id": "loc_warehouse1",
"name": "Main Warehouse",
"quantity": 200,
"available": 188
},
{
"location_id": "loc_store1",
"name": "Downtown Store",
"quantity": 47,
"available": 47
}
],
"updated_at": "2026-01-02T10:30:00Z"
}
}
}
Adjust Inventory
POST /v1/products/:id/inventory/adjust
Request Body:
{
"data": {
"adjustments": [
{
"location_id": "loc_warehouse1",
"quantity": 50,
"reason": "restock",
"reference": "PO-2026-001"
}
]
}
}
Set Inventory
Set absolute inventory levels:
PUT /v1/products/:id/inventory
Request Body:
{
"data": {
"locations": [
{
"location_id": "loc_warehouse1",
"quantity": 250
}
]
}
}
Reserve Inventory
Reserve stock for pending orders:
POST /v1/products/:id/inventory/reserve
Request Body:
{
"data": {
"quantity": 2,
"order_id": "ord_abc123",
"expires_at": "2026-01-02T11:30:00Z"
}
}
Response:
{
"data": {
"type": "reservation",
"id": "res_xyz789",
"attributes": {
"product_id": "prod_abc123",
"quantity": 2,
"order_id": "ord_abc123",
"status": "active",
"expires_at": "2026-01-02T11:30:00Z"
}
}
}
Release Reservation
DELETE /v1/inventory/reservations/:reservation_id
Inventory Locations
Manage warehouse and store locations:
POST /v1/inventory/locations
{
"data": {
"type": "locations",
"attributes": {
"name": "Main Warehouse",
"code": "WH-001",
"type": "warehouse",
"address": {
"line1": "123 Industrial Way",
"city": "Austin",
"state": "TX",
"postal_code": "78701",
"country": "US"
},
"is_default": true
}
}
}
Pricing
Price Tiers
Set up volume-based pricing:
PUT /v1/products/:id/price-tiers
Request Body:
{
"data": {
"tiers": [
{
"min_quantity": 1,
"max_quantity": 9,
"price": { "amount": 29900, "currency": "USD" }
},
{
"min_quantity": 10,
"max_quantity": 49,
"price": { "amount": 27900, "currency": "USD" }
},
{
"min_quantity": 50,
"max_quantity": null,
"price": { "amount": 24900, "currency": "USD" }
}
]
}
}
Customer-Specific Pricing
Set prices for specific customers or groups:
PUT /v1/products/:id/customer-prices
Request Body:
{
"data": {
"prices": [
{
"customer_group_id": "grp_wholesale",
"price": { "amount": 19900, "currency": "USD" }
},
{
"customer_id": "cust_vip123",
"price": { "amount": 22900, "currency": "USD" }
}
]
}
}
Multi-Currency Prices
PUT /v1/products/:id/prices
Request Body:
{
"data": {
"prices": [
{ "amount": 29900, "currency": "USD" },
{ "amount": 27900, "currency": "EUR" },
{ "amount": 3990000, "currency": "JPY" }
]
}
}
Scheduled Pricing
Schedule price changes:
POST /v1/products/:id/scheduled-prices
Request Body:
{
"data": {
"type": "scheduled_price",
"attributes": {
"price": { "amount": 24900, "currency": "USD" },
"compare_at": { "amount": 29900, "currency": "USD" },
"starts_at": "2026-06-01T00:00:00Z",
"ends_at": "2026-06-30T23:59:59Z",
"name": "June Sale"
}
}
}
Product Images
Upload Image
POST /v1/products/:id/images
Request Body (multipart/form-data):
file: [binary image data]
alt_text: "Premium Wireless Headphones - Black"
position: 1
is_primary: true
Response:
{
"data": {
"type": "images",
"id": "img_abc123",
"attributes": {
"url": "https://cdn.23blocks.com/products/prod_abc123/img_abc123.jpg",
"alt_text": "Premium Wireless Headphones - Black",
"position": 1,
"is_primary": true,
"width": 1200,
"height": 1200,
"thumbnails": {
"small": "https://cdn.23blocks.com/products/prod_abc123/img_abc123_100x100.jpg",
"medium": "https://cdn.23blocks.com/products/prod_abc123/img_abc123_400x400.jpg",
"large": "https://cdn.23blocks.com/products/prod_abc123/img_abc123_800x800.jpg"
}
}
}
}
List Images
GET /v1/products/:id/images
Reorder Images
PUT /v1/products/:id/images/reorder
Request Body:
{
"data": {
"image_ids": ["img_xyz789", "img_abc123", "img_def456"]
}
}
Delete Image
DELETE /v1/products/:id/images/:image_id
Product Search
Search Products
GET /v1/products/search
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
q | string | Search query |
filters | object | Facet filters |
sort | string | Sort order |
page | integer | Page number |
per_page | integer | Results per page |
Example:
GET /v1/products/search?q=wireless+headphones&filters[category]=electronics&filters[price][min]=10000&filters[price][max]=50000&sort=relevance
Response:
{
"data": [...],
"meta": {
"total": 45,
"page": 1,
"per_page": 20,
"facets": {
"category": [
{ "value": "electronics", "count": 32 },
{ "value": "audio", "count": 28 }
],
"brand": [
{ "value": "AudioPro", "count": 15 },
{ "value": "SoundMax", "count": 12 }
],
"price_range": [
{ "min": 10000, "max": 20000, "count": 12 },
{ "min": 20000, "max": 30000, "count": 18 },
{ "min": 30000, "max": 50000, "count": 15 }
]
},
"suggestions": ["wireless earbuds", "bluetooth headphones"]
}
}
Search Suggestions
GET /v1/products/search/suggest?q=wire
Response:
{
"data": {
"suggestions": [
"wireless headphones",
"wireless earbuds",
"wireless speakers"
],
"products": [
{
"id": "prod_abc123",
"name": "Premium Wireless Headphones",
"image": "https://cdn.23blocks.com/..."
}
]
}
}
Related Products
Set Related Products
PUT /v1/products/:id/related
Request Body:
{
"data": {
"cross_sells": ["prod_def456", "prod_ghi789"],
"up_sells": ["prod_jkl012"],
"accessories": ["prod_mno345", "prod_pqr678"]
}
}
Get Related Products
GET /v1/products/:id/related
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Type: cross_sells, up_sells, accessories |
limit | integer | Maximum products to return |
Product Bundles
Create Bundle
POST /v1/products
Request Body:
{
"data": {
"type": "products",
"attributes": {
"name": "Complete Audio Setup",
"sku": "BUNDLE-AUDIO-001",
"type": "bundle",
"price": {
"amount": 49900,
"currency": "USD"
},
"bundle_items": [
{
"product_id": "prod_headphones",
"quantity": 1,
"required": true
},
{
"product_id": "prod_case",
"quantity": 1,
"required": true
},
{
"product_id": "prod_cable",
"quantity": 2,
"required": false
}
],
"bundle_pricing": "fixed"
}
}
}
Webhooks
Subscribe to product events:
Available Events
| Event | Description |
|---|---|
product.created | New product created |
product.updated | Product updated |
product.deleted | Product deleted |
product.published | Product status changed to active |
inventory.low_stock | Inventory below threshold |
inventory.out_of_stock | Inventory depleted |
inventory.adjusted | Inventory level changed |
variant.created | New variant created |
variant.updated | Variant updated |
category.updated | Category modified |
Webhook Payload
{
"event": "inventory.low_stock",
"timestamp": "2026-01-02T10:30:00Z",
"data": {
"product_id": "prod_abc123",
"sku": "WH-PRO-2024",
"name": "Premium Wireless Headphones",
"current_quantity": 20,
"threshold": 25,
"location_id": "loc_warehouse1"
}
}
SDK Examples
JavaScript/TypeScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { products: 'https://products.api.us.23blocks.com' },
appId: 'your-app-id',
apiKey: 'your-api-key',
});
// Create product with variants
const product = await client.products.create({
name: 'Cotton T-Shirt',
sku: 'TSHIRT-001',
price: { amount: 2500, currency: 'USD' },
options: [
{ name: 'Size', values: ['S', 'M', 'L', 'XL'] },
{ name: 'Color', values: ['White', 'Black', 'Navy'] }
]
});
// Generate variants from options
await client.products.generateVariants(product.id);
// Search products with facets
const results = await client.products.search({
query: 't-shirt',
filters: {
category: 'clothing',
size: ['M', 'L'],
price: { min: 1000, max: 5000 }
},
sort: 'price_asc'
});
// Check inventory across locations
const inventory = await client.inventory.get(product.id, {
include_locations: true
});
// Reserve inventory for cart
const reservation = await client.inventory.reserve({
product_id: product.id,
quantity: 2,
cart_id: 'cart_abc123',
expires_in: 900 // 15 minutes
});
React Example
import { useProducts, useProduct } from '@23blocks/react';
function ProductGrid() {
const { products, loading, facets, loadMore } = useProducts({
category: 'electronics',
in_stock: true,
per_page: 20
});
if (loading) return <Spinner />;
return (
<div>
<Facets data={facets} />
<div className="grid grid-cols-4 gap-4">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
<button onClick={loadMore}>Load More</button>
</div>
);
}
function ProductDetail({ id }: { id: string }) {
const { product, variants, selectVariant, selectedVariant } = useProduct(id);
return (
<div>
<h1>{product.name}</h1>
<Price amount={selectedVariant?.price || product.price} />
<VariantSelector
variants={variants}
selected={selectedVariant}
onSelect={selectVariant}
/>
<InventoryStatus stock={selectedVariant?.inventory || product.inventory} />
</div>
);
}
Error Codes
| Code | Description |
|---|---|
PRODUCT_NOT_FOUND | Product does not exist |
SKU_ALREADY_EXISTS | SKU is already in use |
INVALID_PRICE | Price format is invalid |
INSUFFICIENT_INVENTORY | Not enough stock available |
INVENTORY_RESERVED | Stock is reserved for other orders |
VARIANT_NOT_FOUND | Variant does not exist |
CATEGORY_NOT_FOUND | Category does not exist |
INVALID_OPTION_VALUES | Variant options don't match product options |
BUNDLE_INVALID | Bundle configuration is invalid |
IMAGE_UPLOAD_FAILED | Failed to upload product image |
Rate Limits
| Endpoint | Limit |
|---|---|
GET /products | 1000/minute |
GET /products/:id | 2000/minute |
POST /products | 100/minute |
PATCH /products/:id | 200/minute |
GET /products/search | 500/minute |
POST /inventory/adjust | 500/minute |
POST /inventory/reserve | 1000/minute |
Best Practices
- SKU Management - Use consistent SKU formats across variants
- Inventory Sync - Set up webhooks for real-time stock updates
- Image Optimization - Use provided thumbnail URLs for performance
- Search Indexing - Include relevant attributes for better search results
- Category Structure - Keep category depth to 3-4 levels maximum
- Price Precision - Always use cents/smallest currency unit
- Reservation Expiry - Set appropriate expiration for cart reservations
Related Resources
- Sales Block - Shopping carts and orders
- Files Block - Product image storage
- Search Block - Advanced search capabilities
- Frontend SDK - Client libraries