> ## 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.

# Add an item to a plan

> Adds one item to a plan. The request body must include `planType` matching the URL `{type}`. Business-rule hooks run for REBALANCE, MARKDOWN, SUPPLIER_RETURN, SUPPLIER_EXCHANGE (blocking failures return HTTP 422; non-blocking warnings appear in `_businessRuleWarnings`). RESTOCK items may be queued for approval when the plan total breaches a policy. PRE_SEASON is not yet supported and returns HTTP 422.



## OpenAPI

````yaml /openapi.json post /api/plans/{type}/{id}/items
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/plans/{type}/{id}/items:
    post:
      tags:
        - Plans
      summary: Add an item to a plan
      description: >-
        Adds one item to a plan. The request body must include `planType`
        matching the URL `{type}`. Business-rule hooks run for REBALANCE,
        MARKDOWN, SUPPLIER_RETURN, SUPPLIER_EXCHANGE (blocking failures return
        HTTP 422; non-blocking warnings appear in `_businessRuleWarnings`).
        RESTOCK items may be queued for approval when the plan total breaches a
        policy. PRE_SEASON is not yet supported and returns HTTP 422.
      operationId: addPlanItem
      parameters:
        - in: path
          name: type
          required: true
          schema:
            description: >-
              Plan type. One of: RESTOCK, REBALANCE, MARKDOWN, SUPPLIER_RETURN,
              SUPPLIER_EXCHANGE, PRE_SEASON. Case-insensitive — "restock" and
              "RESTOCK" are both accepted.
            enum:
              - RESTOCK
              - REBALANCE
              - MARKDOWN
              - SUPPLIER_RETURN
              - SUPPLIER_EXCHANGE
              - PRE_SEASON
            type: string
        - in: path
          name: id
          required: true
          schema:
            description: Unique identifier of the plan (UUID)
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            type: string
        - in: query
          name: page
          required: false
          schema:
            description: Page number (1-based)
            exclusiveMinimum: true
            maximum: 9007199254740991
            type: integer
        - in: query
          name: pageSize
          required: false
          schema:
            description: Items per page
            exclusiveMinimum: true
            maximum: 9007199254740991
            type: integer
        - in: query
          name: search
          required: false
          schema:
            description: Free-text search across variant/product/size labels
            type: string
        - in: query
          name: side
          required: false
          schema:
            description: >-
              Filter by exchange side (RETURN or RECEIVE); only applicable for
              SUPPLIER_EXCHANGE
            enum:
              - RETURN
              - RECEIVE
            type: string
      requestBody:
        content:
          application/json:
            examples:
              addMarkdownItem:
                summary: Add a markdown item
                value:
                  discountPercent: 30
                  planType: MARKDOWN
                  reason: End of season clearance
                  shopId: shop-uuid-paris-01
                  sizeId: size-uuid-l
                  variantId: var-uuid-nike-shirt
              addRebalanceItem:
                summary: Add a rebalance item
                value:
                  planType: REBALANCE
                  quantity: 5
                  reason: Balancing overstock from Paris to Lyon
                  sizeId: size-uuid-m
                  variantId: var-uuid-adidas-jogger
              addRestockItem:
                summary: Add a restock item
                value:
                  negotiationRate: 5
                  planType: RESTOCK
                  quantity: 10
                  reason: Replenishment for Q2
                  shopId: shop-uuid-lyon-02
                  sizeId: size-uuid-42
                  variantId: var-uuid-puma-sneaker
            schema:
              oneOf:
                - properties:
                    negotiationRate:
                      description: >-
                        Negotiated discount rate on the supplier price in
                        percent (0–100); omit to use gross unit cost
                      maximum: 100
                      minimum: 0
                      type: number
                    planType:
                      description: Must be RESTOCK to match the plan type in the URL
                      enum:
                        - RESTOCK
                      type: string
                    quantity:
                      description: Quantity to restock (must be a positive integer)
                      exclusiveMinimum: true
                      maximum: 9007199254740991
                      type: integer
                    reason:
                      description: >-
                        Human-readable reason for this change; written to the
                        audit log
                      maxLength: 500
                      type: string
                    shopId:
                      description: Shop (store) ID where the restock will be fulfilled
                      minLength: 1
                      type: string
                    sizeId:
                      description: Databricks size ID for the item to add
                      minLength: 1
                      type: string
                    variantId:
                      description: Databricks variant ID for the item to add
                      minLength: 1
                      type: string
                  required:
                    - planType
                    - variantId
                    - sizeId
                    - quantity
                    - shopId
                  type: object
                - properties:
                    planType:
                      description: Must be REBALANCE to match the plan type in the URL
                      enum:
                        - REBALANCE
                      type: string
                    quantity:
                      description: Transfer quantity (must be a positive integer)
                      exclusiveMinimum: true
                      maximum: 9007199254740991
                      type: integer
                    reason:
                      description: >-
                        Human-readable reason for this change; written to the
                        audit log
                      maxLength: 500
                      type: string
                    sizeId:
                      description: Databricks size ID for the item to transfer
                      minLength: 1
                      type: string
                    variantId:
                      description: Databricks variant ID for the item to transfer
                      minLength: 1
                      type: string
                  required:
                    - planType
                    - variantId
                    - sizeId
                    - quantity
                  type: object
                - properties:
                    discountPercent:
                      description: >-
                        Markdown discount percentage to apply (0–100, exclusive
                        of 0)
                      exclusiveMinimum: true
                      maximum: 100
                      minimum: 0
                      type: number
                    planType:
                      description: Must be MARKDOWN to match the plan type in the URL
                      enum:
                        - MARKDOWN
                      type: string
                    productId:
                      description: >-
                        Product ID; optional override for the variant's parent
                        product
                      type: string
                    reason:
                      description: >-
                        Human-readable reason for this change; written to the
                        audit log
                      maxLength: 500
                      type: string
                    shopId:
                      description: Shop (store) ID where the markdown applies
                      minLength: 1
                      type: string
                    sizeId:
                      description: Databricks size ID for the item to mark down
                      minLength: 1
                      type: string
                    variantId:
                      description: Databricks variant ID for the item to mark down
                      minLength: 1
                      type: string
                  required:
                    - planType
                    - variantId
                    - sizeId
                    - discountPercent
                    - shopId
                  type: object
                - properties:
                    planType:
                      description: >-
                        Must be SUPPLIER_RETURN to match the plan type in the
                        URL
                      enum:
                        - SUPPLIER_RETURN
                      type: string
                    quantity:
                      description: Return quantity (must be a positive integer)
                      exclusiveMinimum: true
                      maximum: 9007199254740991
                      type: integer
                    reason:
                      description: >-
                        Human-readable reason for this change; written to the
                        audit log
                      maxLength: 500
                      type: string
                    reasonCode:
                      description: >-
                        Reason code for the return. One of: DEFECT_MANUFACTURE,
                        DAMAGE_TRANSIT, WRONG_SKU, WRONG_QUANTITY, RECALL,
                        QC_FAIL, OTHER
                      enum:
                        - DEFECT_MANUFACTURE
                        - DAMAGE_TRANSIT
                        - WRONG_SKU
                        - WRONG_QUANTITY
                        - RECALL
                        - QC_FAIL
                        - OTHER
                      type: string
                    reasonNote:
                      description: >-
                        Required when reasonCode is OTHER; free-text explanation
                        for the return
                      type: string
                    sizeId:
                      description: Databricks size ID for the item to return
                      minLength: 1
                      type: string
                    sourceItemId:
                      description: ID of the originating plan item (RESTOCK or PRE_SEASON)
                      minLength: 1
                      type: string
                    sourceItemType:
                      description: Plan type of the source item (RESTOCK or PRE_SEASON)
                      enum:
                        - RESTOCK
                        - PRE_SEASON
                      type: string
                    sourceShopId:
                      description: >-
                        Fallback shop ID when the source item does not carry a
                        shopId
                      type: string
                    variantId:
                      description: Databricks variant ID for the item to return
                      minLength: 1
                      type: string
                  required:
                    - planType
                    - variantId
                    - sizeId
                    - quantity
                    - sourceItemId
                    - sourceItemType
                    - reasonCode
                  type: object
                - properties:
                    planType:
                      description: >-
                        Must be SUPPLIER_EXCHANGE to match the plan type in the
                        URL
                      enum:
                        - SUPPLIER_EXCHANGE
                      type: string
                    quantity:
                      description: Quantity (must be a positive integer)
                      exclusiveMinimum: true
                      maximum: 9007199254740991
                      type: integer
                    reason:
                      description: >-
                        Human-readable reason for this change; written to the
                        audit log
                      maxLength: 500
                      type: string
                    reasonCode:
                      description: Optional reason code (RETURN side context)
                      type: string
                    side:
                      description: >-
                        Exchange side: RETURN (outgoing to supplier) or RECEIVE
                        (incoming from supplier)
                      enum:
                        - RETURN
                        - RECEIVE
                      type: string
                    sizeId:
                      description: Databricks size ID for the exchanged item
                      minLength: 1
                      type: string
                    sourceItemId:
                      description: >-
                        Source item ID (RETURN side only; forbidden on RECEIVE
                        side)
                      type: string
                    sourceItemType:
                      description: Required when sourceItemId is provided
                      enum:
                        - RESTOCK
                        - PRE_SEASON
                      type: string
                    sourceShopId:
                      description: >-
                        Source shop for RETURN side only; ignored on RECEIVE
                        side
                      type: string
                    unitCost:
                      description: >-
                        Unit cost of the item at exchange time (must be
                        positive)
                      exclusiveMinimum: true
                      minimum: 0
                      type: number
                    variantId:
                      description: Databricks variant ID for the exchanged item
                      minLength: 1
                      type: string
                  required:
                    - planType
                    - side
                    - variantId
                    - sizeId
                    - quantity
                    - unitCost
                  type: object
        required: true
      responses:
        '200':
          content:
            application/json:
              examples:
                added:
                  summary: Item added successfully
                  value:
                    itemId: 550e8400-e29b-41d4-a716-446655440020
              schema:
                properties:
                  data:
                    properties:
                      approvalId:
                        description: >-
                          Approval request ID when queuedForApproval is true
                          (RESTOCK only)
                        type: string
                      itemId:
                        description: ID of the newly created plan item (when applicable)
                        type: string
                      queuedForApproval:
                        description: >-
                          True when the add was routed to approval instead of
                          inserted directly (RESTOCK only)
                        type: boolean
                    type: object
                  success:
                    enum:
                      - true
                    type: boolean
                required:
                  - success
                  - data
                type: object
          description: Successful response
        '400':
          description: >-
            Invalid request parameters, a planType/URL mismatch, or an
            unsupported plan type (PRE_SEASON)
        '401':
          description: Unauthorized — missing or invalid authentication
        '403':
          description: Forbidden — caller lacks the inventoryPlans.manage permission
        '422':
          description: Business-rule violation or rejected by the operations layer
        '500':
          description: Unexpected 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

````