C
Cotizera Docs

Products

Products API

Endpoints for managing your product catalog, including brands and categories for organization.


List Products

GET /api/products

Permission: products:read

Retrieve a paginated list of active products for the current tenant.

Query Parameters:

Parameter Type Required Description
search string No Search by product name or model/SKU (case-insensitive)
brandId string No Filter by brand ID
categoryId string No Filter by category ID
page number No Page number (default: 1)

Response 200 OK:

{
  "data": [
    {
      "id": "clxyz...",
      "name": "Laptop HP ProBook 450",
      "description": "Laptop empresarial con procesador i7",
      "modelSku": "PB450-G10",
      "price": "18500.00",
      "isActive": true,
      "brand": { "id": "...", "name": "HP" },
      "category": { "id": "...", "name": "Laptops" },
      "createdAt": "2026-03-01T10:00:00.000Z"
    }
  ],
  "total": 120,
  "page": 1,
  "totalPages": 6
}

cURL Example:

curl -X GET "https://cotizera.com/api/products?search=laptop&brandId=clxyz&page=1" \
  -H "Cookie: next-auth.session-token=YOUR_TOKEN"

Create Product

POST /api/products

Permission: products:create

Create a new product. If modelSku is provided, it must be unique within the tenant.

Request Body:

{
  "name": "Monitor Dell UltraSharp 27\"",
  "description": "Monitor 4K IPS con USB-C",
  "modelSku": "U2723QE",
  "price": 12500,
  "brandId": "clxyz...",
  "categoryId": "clxyz..."
}
Field Type Required Description
name string Yes Product name (min 1 character)
description string No Product description
modelSku string No Model number or SKU (unique per tenant if provided)
price number Yes Unit price (> 0)
brandId string No Brand ID
categoryId string No Category ID

Response 201 Created: Returns the created product with brand and category included.

Errors:

Status Error When
400 Zod validation message Invalid input
403 "Sin permisos" Role lacks products:create
409 "Ya existe un producto con ese modelo/SKU" Duplicate SKU in tenant

Side Effects:

  • Fires product.created webhook event
  • Triggers onboarding step first_product completion
  • Logs audit entry with action create

Get Single Product

GET /api/products/:id

Permission: products:read

Retrieve a single active product with brand and category details.

Response 200 OK: Returns the product object with brand and category included.

Errors:

Status Error When
404 "Producto no encontrado" Product not found, inactive, or wrong tenant

Update Product

PUT /api/products/:id

Permission: products:update

Update an existing product. If modelSku is changed, uniqueness is re-validated.

Request Body: Same schema as Create Product.

Response 200 OK: Returns the updated product with brand and category.

Errors:

Status Error When
404 "Producto no encontrado" Product not found or inactive
409 "Ya existe un producto con ese modelo/SKU" New SKU conflicts with existing product

Delete Product (Soft Delete)

DELETE /api/products/:id

Permission: products:delete

Soft-deletes a product by setting isActive to false.

Response 200 OK:

{
  "success": true
}

Brands

GET /api/brands

List all active brands for the current tenant, sorted alphabetically.

Response 200 OK:

[
  { "id": "clxyz...", "name": "HP", "tenantId": "...", "isActive": true },
  { "id": "clxyz...", "name": "Dell", "tenantId": "...", "isActive": true }
]

POST /api/brands

Create a new brand. Name must be unique within the tenant.

Request Body:

{
  "name": "Lenovo"
}
Field Type Required Description
name string Yes Brand name (min 1 character)

Response 201 Created: Returns the created brand.

Errors:

Status Error When
409 "Ya existe una marca con ese nombre" Duplicate brand name in tenant

Categories

GET /api/categories

List all active categories for the current tenant, sorted alphabetically.

Response 200 OK:

[
  { "id": "clxyz...", "name": "Laptops", "tenantId": "...", "isActive": true },
  { "id": "clxyz...", "name": "Monitores", "tenantId": "...", "isActive": true }
]

POST /api/categories

Create a new category. Name must be unique within the tenant.

Request Body:

{
  "name": "Impresoras"
}
Field Type Required Description
name string Yes Category name (min 1 character)

Response 201 Created: Returns the created category.

Errors:

Status Error When
409 "Ya existe una categoria con ese nombre" Duplicate category name in tenant
© 2026 Cotizera. All rights reserved.