GET /api/products
Lists products in the authenticated account's organization with optional filters, sorting, and pagination.
Request
GET /api/products?limit=25&offset=0&q=widget&sku=WIDGET-A&productCategoryId=uuid&hasInventory=true&minQuantity=5&minUnitCost=10&maxUnitCost=50&sortBy=unitCost&sortOrder=asc
Response 200
{
"products": [
{
"id": "uuid",
"ownerId": "uuid",
"productCategoryId": "uuid",
"name": "Widget A",
"description": "Main warehouse item",
"sku": "WIDGET-A",
"unitCost": 19.99,
"createdAt": "2026-03-28T00:00:00.000Z",
"updatedAt": "2026-03-29T12:00:00.000Z"
}
],
"pagination": {
"limit": 25,
"offset": 0,
"total": 1,
"hasMore": false
}
}
Supported filters
- limit (default 50, max 200)
- offset (default 0)
- q
- sku
- productCategoryId
- createdFrom
- createdTo
- updatedFrom
- updatedTo
- hasInventory (true or false)
- minQuantity
- maxQuantity
- minUnitCost
- maxUnitCost
- sortBy (name, sku, createdAt, updatedAt, quantity, unitCost)
- sortOrder (asc, desc)
GET /api/products/:productId
Returns a single product by its identifier.
Request
GET /api/products/4cf7f6ef-1111-2222-3333-444444444444
Response 200
{
"product": {
"id": "4cf7f6ef-1111-2222-3333-444444444444",
"ownerId": "uuid",
"productCategoryId": "uuid",
"name": "Widget A",
"description": "Main warehouse item",
"sku": "WIDGET-A",
"unitCost": 19.99,
"createdAt": "2026-03-28T00:00:00.000Z",
"updatedAt": "2026-03-29T12:00:00.000Z"
}
}
POST /api/uploads/products/presign
Creates a presigned S3 upload URL for direct product image uploads. The client uploads to S3 first, then stores the returned fileUrl on the product as imageUrl.
Request
POST /api/uploads/products/presign
Content-Type: application/json
{
"filename": "widget-a.jpg",
"contentType": "image/jpeg"
}
Response 200
{
"uploadUrl": "https://bucket.s3.amazonaws.com/...",
"fileUrl": "https://bucket.s3.amazonaws.com/products/org-id/123-widget-a.jpg",
"objectKey": "products/org-id/123-widget-a.jpg",
"expiresIn": 900
}
Supported content types
- image/jpeg
- image/png
- image/webp
- image/gif
POST /api/products
Creates a new product.
Request
POST /api/products
Content-Type: application/json
{
"name": "Widget A",
"description": "Main warehouse item",
"sku": "WIDGET-A",
"imageUrl": "https://bucket.s3.amazonaws.com/products/org-id/widget-a.jpg",
"productCategoryId": "uuid",
"unitCost": 19.99
}
Response 201
{
"product": {
"id": "uuid",
"ownerId": "uuid",
"productCategoryId": "uuid",
"name": "Widget A",
"description": "Main warehouse item",
"sku": "WIDGET-A",
"imageUrl": "https://bucket.s3.amazonaws.com/products/org-id/widget-a.jpg",
"unitCost": 19.99,
"createdAt": "2026-03-28T00:00:00.000Z",
"updatedAt": "2026-03-28T00:00:00.000Z"
}
}
PUT /api/products/:productId
Updates an existing product record.
Request
PUT /api/products/4cf7f6ef-1111-2222-3333-444444444444
Content-Type: application/json
{
"name": "Widget A Prime",
"description": "Updated item",
"sku": "WIDGET-A-PRIME",
"imageUrl": "https://bucket.s3.amazonaws.com/products/org-id/widget-a-prime.jpg",
"productCategoryId": null,
"unitCost": 24.50
}
Response 200
{
"product": {
"id": "4cf7f6ef-1111-2222-3333-444444444444",
"ownerId": "uuid",
"productCategoryId": null,
"name": "Widget A Prime",
"description": "Updated item",
"sku": "WIDGET-A-PRIME",
"unitCost": 24.5,
"createdAt": "2026-03-28T00:00:00.000Z",
"updatedAt": "2026-03-29T12:00:00.000Z"
}
}
DELETE /api/products/:productId
Deletes a product in the current organization when the caller has product delete permission.
Request
DELETE /api/products/4cf7f6ef-1111-2222-3333-444444444444
Response 200
{
"product": {
"id": "4cf7f6ef-1111-2222-3333-444444444444",
"ownerId": "uuid",
"productCategoryId": null,
"name": "Widget A Prime",
"description": "Updated item",
"sku": "WIDGET-A-PRIME",
"imageUrl": "https://bucket.s3.amazonaws.com/products/org-id/widget-a-prime.jpg",
"unitCost": 24.5,
"createdAt": "2026-03-28T00:00:00.000Z",
"updatedAt": "2026-03-29T12:00:00.000Z"
}
}