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

# List products with sales and stock performance

> Returns a paginated, searchable list of products (the catalog) where each row is enriched with sales-performance and stock metrics: units sold, gross and net sales (revenue), gross margin and margin %, monthly sales, average selling price, stock value, on-hand quantity, and merchandising tags (e.g. overstock / slow-mover). Use this to review product performance, find best-selling / top-selling products by revenue or units, spot overstock and slow-movers, or resolve a product identifier. To rank best-sellers, sort with `sortBy` + `sortDirection=desc`: `sortBy=totalNetSales` (net revenue), `sortBy=totalGrossSales` (gross revenue), or `sortBy=totalUnitsSold` (units sold); the default `sortBy=name` returns products alphabetically, NOT by performance. Filter by brand or free-text search on the product name. (For an AI-ranked list of which products to restock, rebalance, or mark down, use GET /intelligence/decision-vectors; for stock-out urgency see GET /inventory/risks.) The KPI columns are read from the gold dim_product_summary tables, which are pre-aggregated PER time_period (~13 rows per product, one per period). The `period` query param selects the window (default LAST_30_DAYS) and the response always reflects exactly one period — a read that did not pin a single period would fan out and return inflated counts and totals.



## OpenAPI

````yaml /openapi.json get /api/products
openapi: 3.0.3
info:
  contact:
    email: dev@solya.io
    name: Solya Team
  description: >-

    # Solya API


    Solya is a fashion retail inventory management platform for buyers and
    merchandisers.

    This API exposes all capabilities needed to manage the inventory lifecycle:
    catalog

    browsing, risk detection, plan creation (Restock / Markdown / Rebalance),
    analytics

    queries, and data-platform operations.


    ## Authentication


    Three authentication schemes are supported:


    | Scheme | Header | Use case |

    |---|---|---|

    | **BearerAuth** | `Authorization: Bearer <nextauth-token>` | Human users
    via the Solya web app (NextAuth session) |

    | **InternalBearerAuth** | `Authorization: Bearer <static-token>` | Internal
    jobs and cron tasks (static token per service) |

    | **ServiceAccountToken** | `Authorization: Bearer solya_sa_*` | LLM agents
    and programmatic clients (opaque token created via Settings) |


    All endpoints except `GET /api/health` require one of the above.

    See [Agent authentication guide](/docs/api/AGENT_AUTH.md) for the Service
    Account token flow.


    ## Response format


    Every endpoint returns an `ActionResponse<T>` envelope:


    **Success:**

    ```json

    { "success": true, "data": { ... } }

    ```


    **Error:**

    ```json

    { "success": false, "errorCode": "PLAN_NOT_FOUND", "error": "Human-readable
    message" }

    ```


    The `errorCode` is a stable machine-readable string (see common error codes
    below).

    The `error` field is for human display only and may change between versions.


    ## Pagination


    List endpoints accept `page` (1-indexed, default 1) and `pageSize` (default
    20, max 100).

    Responses include a `total` field with the total number of matching records.


    ```

    GET /api/shops?page=2&pageSize=50

    → { "success": true, "data": { "data": [...], "total": 120, "page": 2,
    "pageSize": 50 } }

    ```


    ## Common error codes


    | HTTP status | errorCode | Meaning |

    |---|---|---|

    | 401 | `UNAUTHORIZED` | Token missing, expired, or invalid |

    | 403 | `FORBIDDEN` | Token valid but lacks the required permission or scope
    |

    | 404 | `NOT_FOUND` | Requested resource does not exist |

    | 409 | `BUSINESS_RULE_VIOLATION` | Business rule blocked the operation (see
    response details) |

    | 422 | `VALIDATION_ERROR` | Input failed Zod schema validation |

    | 429 | `RATE_LIMIT_EXCEEDED` | Rate limit hit (100 req/min per token) |

    | 500 | `INTERNAL_ERROR` | Unexpected server error |


    ## Rate limiting


    Default limit: **100 requests per minute** per authentication token.

    When the limit is exceeded the API returns HTTP 429 with `errorCode:
    "RATE_LIMIT_EXCEEDED"`.

    Agents should implement exponential back-off and respect the `Retry-After`
    header when present.


    ## Further reading


    See the [Agent guide](/docs/api/AGENT_GUIDE.md) for end-to-end workflows,
    call-chaining

    patterns, and anti-patterns to avoid.
        
  license:
    name: Proprietary
    url: https://solya.app/terms
  title: Solya API
  version: 1.0.0
  x-ai-context: >-
    Solya is a fashion retail inventory management platform for retailers.

    Core concepts:

    - **Organization** (tenant): every endpoint is scoped by organizationId
    extracted from the token.

    - **Shop**: a physical point of sale belonging to the organization.

    - **ProductVariant**: a SKU — a product variant with size and color.

    - **Plan**: a Restock / Markdown / Rebalance grouping PlanItems to
    orchestrate inventory decisions.


    Typical agent workflow:

    1. List the organization's shops — GET /api/shops

    2. List variants at risk (stockout, overstock, slow-mover) — GET
    /api/inventory/risks

    3. Create a plan — POST /api/restock-plans, /api/markdown-plans, or
    /api/rebalance-plans

    4. Add items to the plan — POST /api/restock-plans/{planId}/items (or
    equivalent for other plan types)

    5. Validate / submit the plan via the corresponding action endpoint


    Auth: use a Service Account token (see /docs/api/AGENT_AUTH.md).

    The token is created by an org admin via Settings and has the format
    solya_sa_<43 base64url chars>.


    All responses follow the ActionResponse envelope:

    - Success: { success: true, data: T }

    - Error:   { success: false, errorCode: string, error: string }


    Use the errorCode field to drive retry logic or surface user-facing
    messages.
servers:
  - description: Current environment
    url: https://app.solya.app
security:
  - BearerAuth: []
tags:
  - description: >-
      Health and liveness endpoints. Use GET /api/health to verify the service
      is reachable before starting a workflow. No authentication required.
    name: System
  - description: >-
      Physical points of sale belonging to the organization. Supports listing,
      creation, update, and deactivation. Shops are referenced by all Plan types
      (Restock, Markdown, Rebalance) and by every inventory analytics endpoint.
    name: Shops
  - description: >-
      Product brands configured for the organization. Brands are used to filter
      catalog queries and analytics. Supports CRUD operations.
    name: Brands
  - description: >-
      The product catalog: style-level entities grouping one or more
      ProductVariants. Supports listing with rich filter options (brand, family,
      gender, season) and individual retrieval.
    name: Products
  - description: >-
      SKU-level product entities (a Product with a specific size and color).
      Variants are the atomic unit referenced by PlanItems, inventory risk
      alerts, and analytics queries.
    name: Variants
  - description: >-
      Curated product groupings used for seasonal assortment management. A
      Collection groups Variants and can be referenced when creating or
      filtering Plans.
    name: Collections
  - description: >-
      Current on-hand stock records per Variant per Shop. Used to understand the
      live inventory position before creating a restock or rebalance plan.
    name: Inventory Items
  - description: >-
      AI-detected inventory risk signals: stockout risk, overstock, slow-movers,
      and displaced stock. The primary input for agents building
      recommendation-driven plans. Supports filtering by shop, brand, risk type,
      and severity.
    name: Inventory Risks
  - description: >-
      AI-generated restock quantity recommendations per Variant per Shop.
      Consumed by agents to pre-populate Restock plan items. Based on sales
      velocity, stock coverage, and lead time.
    name: Recommendations - Restock
  - description: >-
      AI-generated markdown discount recommendations for slow-moving or
      overstock Variants. Consumed by agents to pre-populate Markdown plan
      items. Includes recommended discount rate and expected clearance timeline.
    name: Recommendations - Markdown
  - description: >-
      AI-generated stock transfer recommendations between shops to balance
      supply with demand. Consumed by agents to pre-populate Rebalance plan
      items.
    name: Recommendations - Rebalance
  - description: >-
      Historical sales transaction lines at the Variant + Shop + date level.
      Used by analytics and by the AI recommendation engine. Supports date-range
      and multi-dimensional filtering.
    name: Sales Lines
  - description: >-
      Purchase order lines tracking inbound stock from suppliers. Combined with
      stock and sales data to compute forward coverage and restock needs.
    name: Order Lines
  - description: >-
      Inventory movement records (transfers, returns, adjustments). Used to
      reconcile the stock ledger and audit stock changes between shops.
    name: Movement Lines
  - description: >-
      Running stock balance log per Variant per Shop. Provides a point-in-time
      view of stock levels and is the source of truth for coverage computations.
    name: Stock Ledger
  - description: >-
      Rebalance plans orchestrate stock transfers between shops. Supports
      creating plans, adding Variant items with proposed transfer quantities,
      reviewing, and submitting. Business rules are evaluated on item addition.
    name: Plans - Rebalance
  - description: >-
      Restock plans (order plans) orchestrate purchase orders to suppliers.
      Supports creating sessions, adding Variant items with proposed order
      quantities, reviewing totals, and submitting. Integrates with the order
      plan workflow.
    name: Plans - Restock
  - description: >-
      Autocomplete and typeahead search endpoints for catalog dimensions:
      products, brands, shops, sizes, families, genders, and more. Designed for
      fast UI search (low latency, small result sets). Use catalog list
      endpoints for full paginated access.
    name: Search
  - description: >-
      Manage file-based data ingestion: upload CSV/XLSX files, poll ingestion
      status, list historical ingestion runs, and trigger batch reprocessing.
      Used by the data team to import POS data and catalog updates.
    name: Data Platform - File Ingestions
  - description: >-
      Organization-level configuration for the data platform: data source
      connections, POS integration settings, and ingestion schedules. Requires
      elevated permissions.
    name: Data Platform - Settings
  - description: >-
      Configuration of automated inventory alerts: threshold-based rules that
      monitor stock levels, sales velocity, and coverage gaps. Supports CRUD for
      alert definitions; alert evaluation runs are triggered by the data
      platform scheduler.
    name: Data Platform - Alerts
  - description: >-
      Endpoints designed for LLM agents and programmatic clients. These
      endpoints expose agent-optimized response shapes. Authenticate with a
      Service Account token (format: solya_sa_*) created via Settings → API
      Tokens.
    name: Agent
externalDocs:
  description: >-
    Complete guide for LLM agents and programmatic clients: authentication,
    pagination patterns, ActionResponse interpretation, call chaining, business
    rule error handling.
  url: /docs/api/AGENT_GUIDE.md
paths:
  /api/products:
    get:
      tags:
        - Products
      summary: List products with sales and stock performance
      description: >-
        Returns a paginated, searchable list of products (the catalog) where
        each row is enriched with sales-performance and stock metrics: units
        sold, gross and net sales (revenue), gross margin and margin %, monthly
        sales, average selling price, stock value, on-hand quantity, and
        merchandising tags (e.g. overstock / slow-mover). Use this to review
        product performance, find best-selling / top-selling products by revenue
        or units, spot overstock and slow-movers, or resolve a product
        identifier. To rank best-sellers, sort with `sortBy` +
        `sortDirection=desc`: `sortBy=totalNetSales` (net revenue),
        `sortBy=totalGrossSales` (gross revenue), or `sortBy=totalUnitsSold`
        (units sold); the default `sortBy=name` returns products alphabetically,
        NOT by performance. Filter by brand or free-text search on the product
        name. (For an AI-ranked list of which products to restock, rebalance, or
        mark down, use GET /intelligence/decision-vectors; for stock-out urgency
        see GET /inventory/risks.) The KPI columns are read from the gold
        dim_product_summary tables, which are pre-aggregated PER time_period
        (~13 rows per product, one per period). The `period` query param selects
        the window (default LAST_30_DAYS) and the response always reflects
        exactly one period — a read that did not pin a single period would fan
        out and return inflated counts and totals.
      operationId: listProducts
      parameters:
        - in: query
          name: page
          required: false
          schema:
            default: 1
            description: 'Page number, 1-indexed (default: 1)'
            maximum: 9007199254740991
            minimum: 1
            type: integer
        - in: query
          name: pageSize
          required: false
          schema:
            default: 20
            description: 'Number of items per page, max 100 (default: 20)'
            maximum: 100
            minimum: 1
            type: integer
        - in: query
          name: q
          required: false
          schema:
            description: Case-insensitive substring search query
            type: string
        - in: query
          name: brandId
          required: false
          schema:
            type: string
        - in: query
          name: period
          required: false
          schema:
            description: >-
              Pre-aggregated time window to read product KPIs for (defaults to
              LAST_30_DAYS). The gold dim_product_summary tables hold one row
              per product per time_period; this selects which window's
              aggregates (sales, units, margin, stock) are returned. Choosing a
              different period changes the metric values, not the set of
              products.
            enum:
              - LAST_7_DAYS
              - LAST_30_DAYS
              - LAST_90_DAYS
              - LAST_6_MONTHS
              - LAST_12_MONTHS
              - THIS_MONTH
              - THIS_QUARTER
              - THIS_YEAR
              - YEAR_TO_DATE
              - LAST_MONTH
              - LAST_QUARTER
              - LAST_YEAR
              - ALL_TIME
            type: string
        - in: query
          name: sortBy
          required: false
          schema:
            default: name
            description: >-
              Column to sort by. Defaults to 'name' (alphabetical). For
              best-selling / top-selling products, use a sales metric:
              'totalNetSales' (net revenue), 'totalGrossSales' (gross revenue),
              or 'totalUnitsSold' (units sold), combined with
              sortDirection=desc. Other metrics: 'totalQuantity' (stock on
              hand), 'averagePrice', 'monthlySales', 'stockValue',
              'totalDiscount', 'totalCogs', 'totalGrossMargin',
              'totalTransactions', 'avgGrossMarginPct', 'avgSellingPrice',
              'variantCount', 'shopCount', plus the 'brandName' and 'family'
              dimensions.
            enum:
              - name
              - brandName
              - family
              - totalQuantity
              - averagePrice
              - monthlySales
              - stockValue
              - totalGrossSales
              - totalNetSales
              - totalUnitsSold
              - totalDiscount
              - totalCogs
              - totalGrossMargin
              - totalTransactions
              - avgGrossMarginPct
              - avgSellingPrice
              - variantCount
              - shopCount
            type: string
        - in: query
          name: sortDirection
          required: false
          schema:
            default: asc
            description: >-
              Sort direction: 'asc' or 'desc'. Defaults to 'asc'. Use 'desc'
              with a sales-metric sortBy (e.g. totalNetSales) to rank
              best-sellers first.
            enum:
              - asc
              - desc
            type: string
      responses:
        '200':
          content:
            application/json:
              examples:
                sample:
                  summary: Two products matching a search for Sézane dresses
                  value:
                    data:
                      - averagePrice: 122.6
                        avgGrossMarginPct: 55
                        avgSellingPrice: 122.65
                        brandName: Sézane
                        family: Robes
                        id: product-uuid-1
                        imageUrl: https://cdn.example.com/products/robe-angele.jpg
                        monthlySales: 23
                        name: Robe Angèle
                        shopCount: 6
                        sizesAvailable:
                          - XS
                          - S
                          - M
                          - L
                        stockValue: 2473.49
                        tags:
                          - Overstock - +30 jours
                        totalCogs: 1269.38
                        totalDiscount: 0
                        totalGrossMargin: 1551.52
                        totalGrossSales: 2820.9
                        totalNetSales: 2820.9
                        totalQuantity: 217
                        totalTransactions: 17
                        totalUnitsSold: 23
                        variantCount: 4
                      - averagePrice: 85
                        avgGrossMarginPct: 55
                        avgSellingPrice: 85
                        brandName: Sézane
                        family: Chemises
                        id: product-uuid-2
                        imageUrl: https://cdn.example.com/products/chemise-adele.jpg
                        monthlySales: 11
                        name: Chemise Adèle
                        shopCount: 4
                        sizesAvailable:
                          - S
                          - M
                          - L
                        stockValue: 1078
                        tags: []
                        totalCogs: 420
                        totalDiscount: 0
                        totalGrossMargin: 515
                        totalGrossSales: 935
                        totalNetSales: 935
                        totalQuantity: 98
                        totalTransactions: 9
                        totalUnitsSold: 11
                        variantCount: 3
                    page: 1
                    pageSize: 20
                    total: 58
              schema:
                properties:
                  data:
                    properties:
                      data:
                        items:
                          properties:
                            averagePrice:
                              description: Average selling price across all variants
                              nullable: true
                              type: number
                            avgGrossMarginPct:
                              description: Average gross margin percentage (0–100)
                              nullable: true
                              type: number
                            avgSellingPrice:
                              description: Average selling price in the selected period
                              nullable: true
                              type: number
                            brandName:
                              description: Name of the brand
                              nullable: true
                              type: string
                            family:
                              description: Taxonomy family path (slash-separated hierarchy)
                              nullable: true
                              type: string
                            id:
                              description: Unique product identifier (UUID)
                              type: string
                            imageUrl:
                              description: URL of the product image
                              nullable: true
                              type: string
                            monthlySales:
                              description: Number of units sold in the current month
                              nullable: true
                              type: number
                            name:
                              description: Display name of the product
                              nullable: true
                              type: string
                            shopCount:
                              description: Number of shops carrying this product
                              nullable: true
                              type: number
                            sizesAvailable:
                              description: List of available size labels
                              items:
                                type: string
                              type: array
                            stockValue:
                              description: Total stock value at average unit cost
                              nullable: true
                              type: number
                            tags:
                              description: >-
                                List of risk/performance tags assigned to this
                                product
                              items:
                                type: string
                              nullable: true
                              type: array
                            totalCogs:
                              description: Total cost of goods sold in the selected period
                              nullable: true
                              type: number
                            totalDiscount:
                              description: >-
                                Total discount amount applied in the selected
                                period
                              nullable: true
                              type: number
                            totalGrossMargin:
                              description: >-
                                Total gross margin (sales minus COGS) in the
                                selected period
                              nullable: true
                              type: number
                            totalGrossSales:
                              description: Total gross sales amount in the selected period
                              nullable: true
                              type: number
                            totalNetSales:
                              description: >-
                                Total net sales after returns in the selected
                                period
                              nullable: true
                              type: number
                            totalQuantity:
                              description: Total stock quantity across all shops and sizes
                              nullable: true
                              type: number
                            totalTransactions:
                              description: Number of transactions in the selected period
                              nullable: true
                              type: number
                            totalUnitsSold:
                              description: Total units sold in the selected period
                              nullable: true
                              type: number
                            variantCount:
                              description: Number of active variants for this product
                              nullable: true
                              type: number
                          required:
                            - id
                            - name
                            - brandName
                            - family
                            - sizesAvailable
                            - totalQuantity
                            - averagePrice
                            - imageUrl
                            - monthlySales
                            - stockValue
                            - totalGrossSales
                            - totalNetSales
                            - totalUnitsSold
                            - totalDiscount
                            - totalCogs
                            - totalGrossMargin
                            - totalTransactions
                            - avgGrossMarginPct
                            - avgSellingPrice
                            - variantCount
                            - shopCount
                            - tags
                          type: object
                        type: array
                      page:
                        type: number
                      pageSize:
                        type: number
                      total:
                        type: number
                    required:
                      - data
                      - total
                      - page
                      - pageSize
                    type: object
                  success:
                    enum:
                      - true
                    type: boolean
                required:
                  - success
                  - data
                type: object
          description: Successful response
        '400':
          description: Validation error
        '401':
          description: Unauthorized
        '500':
          description: Internal server error
      security:
        - BearerAuth: []
components:
  securitySchemes:
    BearerAuth:
      bearerFormat: JWT
      description: >-
        User session token issued by NextAuth. For human users accessing Solya
        via the web application.
      scheme: bearer
      type: http

````