openapi: 3.0.3
info:
  title: WineSitting API
  version: v2
  description: |
    The WineSitting REST API lets authenticated customers manage their account,
    addresses and orders, and retrieve cellar, putaway, invoice and transaction data.

    Requests and responses use JSON unless an operation explicitly documents a file upload or download.
servers:
  - url: https://app.winesitting.com/api/v2
    description: Production
  - url: https://preprod.winesitting.com/api/v2
    description: Preproduction
security:
  - WineSittingApiKey: []
tags:
  - name: Status
    description: Check that the WineSitting API is available.
  - name: Account
    description: Retrieve and update the authenticated WineSitting account.
  - name: Addresses
    description: Manage delivery addresses belonging to the authenticated account.
  - name: Warehouses
    description: Retrieve WineSitting storage warehouses and their public information.
  - name: Bottles
    description: |
      Retrieve the cellar as consolidated wine, format and packaging references.
      A logistic reference uses the `wine_id|format|box` format, for example `1234|0.75|bottles`.
  - name: Orders
    description: Create and retrieve orders, calculate delivery options and manage order attachments.
  - name: Putaways
    description: Retrieve putaways and their attachments.
  - name: Invoices
    description: Retrieve invoices belonging to the authenticated account.
  - name: Transactions
    description: Retrieve cellar movements belonging to the authenticated account.
  - name: Enums
    description: Retrieve the supported values for account preferences and resource statuses.
paths:
  /status:
    get:
      tags: [Status]
      x-position: 1
      operationId: getApiStatus
      summary: Check API status
      description: Returns an empty JSON array when the API is available.
      responses:
        '200':
          description: The API is available.
          content:
            application/json:
              schema:
                type: array
                maxItems: 0
                items: {}
              example: []
        '401':
          $ref: '#/components/responses/Unauthorized'

  /account:
    get:
      tags: [Account]
      x-position: 1
      operationId: getCurrentAccount
      summary: Get current account information
      responses:
        '200':
          description: Current account information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Account'
              example:
                firstname: Test
                lastname: Test
                company: WineSitting
                email: email@domain.com
                warehouse:
                  id: 4
                  public_name: WineSitting Wissous B
                  street1: 14/16 bd Arago
                  street2: ''
                  city: Wissous
                  zipcode: '91320'
                  phone: '+33188400480'
                  email: contact@winesitting.com
                  country: FR
                  information: null
                  time_slots: null
                  excise_number: 'FR00000000000'
                parent: null
                email_billing: [email@domain.com, email2@domain.com]
                email_stock: [email@domain.com]
                minimum_stock: 30
                email_preference: [invoiceValidated]
                cellar_preference: [potential]
                delivery_slip_preference: [box, comment]
        '401':
          $ref: '#/components/responses/Unauthorized'
    put:
      tags: [Account]
      x-position: 2
      operationId: updateCurrentAccount
      summary: Update current account information
      description: Send the complete account payload. Preference values are available from the Enums operation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountUpdate'
            example:
              firstname: Test
              lastname: Test
              company: WineSitting
              email: email@domain.com
              email_billing: [email@domain.com, email2@domain.com]
              email_stock: [email@domain.com]
              minimum_stock: 30
              email_preference: [invoiceValidated]
              cellar_preference: [potential]
              delivery_slip_preference: [box, comment]
              delivery_slip_sort_preference: wine
      responses:
        '200':
          description: Updated account information.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Account'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationFailed'

  /addresses:
    get:
      tags: [Addresses]
      x-position: 1
      operationId: listAddresses
      summary: Get all addresses
      description: Retrieves the addresses associated with the current account.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/OrderDirection'
      responses:
        '200':
          description: Addresses returned successfully.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/TotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Address'
              example:
                - id: 2531
                  firstname: Sherlock
                  lastname: Holmes
                  company: Sherlock Holmes Investigation Ltd.
                  street1: 221b Baker Street
                  street2: null
                  city: Londres
                  zipcode: AB10 1
                  country: GB
                  phone: '+44132456798'
                  email: email@domain.tld
                  vat_number: null
                  detail: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                  reference: My reference
                - id: 1429
                  firstname: Astérix
                  lastname: Obelix
                  company: La Gauloise du Sanglier
                  street1: Village insoumis
                  street2: null
                  city: Armorique
                  zipcode: '59000'
                  country: FR
                  phone: '+33234567891'
                  email: email@domain.tld
                  vat_number: null
                  detail: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                  reference: My reference
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Addresses]
      x-position: 3
      operationId: createAddress
      summary: Create an address
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressInput'
            example:
              firstname: Sherlock
              lastname: Holmes
              company: Sherlock Holmes Investigation Ltd.
              street1: 221b Baker Street
              street2: null
              city: Londres
              zipcode: AB10 1
              country: GB
              phone: '+44132456798'
              email: email@domain.tld
              vatNumber: null
              detail: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              reference: My new reference
      responses:
        '200':
          description: Address created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Address'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationFailed'

  /addresses/{id}:
    parameters:
      - $ref: '#/components/parameters/AddressId'
    get:
      tags: [Addresses]
      x-position: 2
      operationId: getAddress
      summary: Get a specific address
      responses:
        '200':
          description: Address returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Address'
              example:
                id: 2531
                firstname: Sherlock
                lastname: Holmes
                company: Sherlock Holmes Investigation Ltd.
                street1: 221b Baker Street
                street2: null
                city: Londres
                zipcode: AB10 1
                country: GB
                phone: '+44132456798'
                email: email@domain.tld
                vat_number: null
                detail: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                reference: My reference
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags: [Addresses]
      x-position: 4
      operationId: updateAddress
      summary: Update an address
      description: Send all address fields, including unchanged values.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddressInput'
            example:
              firstname: Sherlock
              lastname: Holmes
              company: Sherlock Holmes Investigation Ltd.
              street1: 221b Baker Street
              street2: null
              city: Londres
              zipcode: AB10 1
              country: GB
              phone: '+44132456798'
              email: email@domain.tld
              vatNumber: null
              detail: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
              reference: My reference
      responses:
        '200':
          description: Address updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Address'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationFailed'
    delete:
      tags: [Addresses]
      x-position: 5
      operationId: deleteAddress
      summary: Delete an address
      description: The billing address cannot be deleted.
      responses:
        '200':
          description: Deleted address. Its identifier is null after deletion.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Address'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /warehouses:
    get:
      tags: [Warehouses]
      x-position: 1
      operationId: listWarehouses
      summary: Get all active storage warehouses
      description: Returns WineSitting warehouses that are active and available for storage.
      responses:
        '200':
          description: Active storage warehouses returned successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Warehouse'
              example:
                - id: 4
                  public_name: WineSitting Wissous B
                  street1: 14/16 bd Arago
                  street2: ''
                  city: Wissous
                  zipcode: '91320'
                  phone: '+33188400480'
                  email: contact@winesitting.com
                  country: FR
                  information: null
                  time_slots: null
                  excise_number: 'FR00000000000'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /warehouses/{id}:
    get:
      tags: [Warehouses]
      x-position: 2
      operationId: getWarehouse
      summary: Get a warehouse
      parameters:
        - $ref: '#/components/parameters/WarehouseId'
      responses:
        '200':
          description: Warehouse returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Warehouse'
              example:
                id: 4
                public_name: WineSitting Wissous B
                street1: 14/16 bd Arago
                street2: ''
                city: Wissous
                zipcode: '91320'
                phone: '+33188400480'
                email: contact@winesitting.com
                country: FR
                information: null
                time_slots: null
                excise_number: 'FR00000000000'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'

  /bottles:
    get:
      tags: [Bottles]
      x-position: 1
      operationId: listBottles
      summary: Get all bottles
      description: This endpoint is not paginated and returns the cellar in consolidated form.
      responses:
        '200':
          description: Consolidated cellar returned successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BottleReference'
              example:
                - id: 89878
                  wine: Jean Michel Sorbe
                  color: Blanc
                  country: FR
                  region: Loire
                  appelation: Quincy
                  description: null
                  ranking: null
                  vintage: 2020
                  alcohol: 13
                  format: 0.75
                  price: null
                  comment: null
                  quantity: 1
                  box: bottles
                  reference: 89878|0.75|bottles
                - id: 10376
                  wine: Chateau Rayas
                  color: Rouge
                  country: FR
                  region: Rhône
                  appelation: Chateauneuf du Pape
                  description: null
                  ranking: null
                  vintage: 2000
                  alcohol: 13.5
                  format: 0.75
                  price: null
                  comment: null
                  quantity: 12
                  box: CTO12
                  reference: 10376|0.75|CTO12
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders:
    get:
      tags: [Orders]
      x-position: 1
      operationId: listOrders
      summary: Get all orders
      description: |
        Lists orders using the shared pagination parameters.
        Tracking pages can be built as `https://winesitting.aftership.com/{tracking_number}?lang=fr`.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/OrderDirection'
      responses:
        '200':
          description: Orders returned successfully.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/TotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Order'
              example:
                - id: 109951
                  comment: ABCD
                  is_pickup: true
                  address: null
                  pickup_warehouse:
                    id: 6
                    public_name: WineSitting Chilly-Mazarin
                    street1: 2 rue Guynemer
                    street2: ''
                    city: Chilly-Mazarin
                    zipcode: '91380'
                    phone: '+33123456789'
                    email: contact@winesitting.com
                    country: FR
                    information: null
                    time_slots: null
                    excise_number: 'FR00000000000'
                  warehouse:
                    id: 6
                    public_name: WineSitting Chilly-Mazarin
                    street1: 2 rue Guynemer
                    street2: ''
                    city: Chilly-Mazarin
                    zipcode: '91380'
                    phone: '+33123456789'
                    email: contact@winesitting.com
                    country: FR
                    information: null
                    time_slots: null
                    excise_number: 'FR00000000000'
                  attachments: []
                  delivery_date:
                    date: '2024-09-11 00:00:00.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  delivery_start: '07:30'
                  delivery_end: '16:30'
                  preparation_date:
                    date: '2024-09-10 00:00:00.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  tracking_numbers: []
                  carrier: Pickup
                  status:
                    value: 0
                    name: NEW
                    label: En attente de préparation
                  additional_status: null
                  reference: null
                  origin: API
                  created_by: Test Test
                  updated_by: Test Test
                  created_at:
                    date: '2024-09-09 18:05:12.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  updated_at:
                    date: '2024-09-09 18:05:14.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  content:
                    - id: 78545
                      wine: Le Nez de Goth & Bieres Ney
                      color: Biere
                      country: FR
                      region: Jura
                      appelation: Biere
                      description: Trousotte
                      ranking: null
                      vintage: 2020
                      alcohol: 5
                      format: 0.33
                      price: null
                      comment: null
                      quantity: 1
                      box: bottles
                      reference: 78545|0.33|bottles
                  errors: null
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Orders]
      x-position: 3
      operationId: createOrder
      summary: Create an order
      description: |
        Creates an order. If `delivery_date` is omitted, WineSitting chooses the earliest valid delivery date.
        Unless `allow_partial_fulfillment` is enabled, every requested bottle must be available in sufficient quantity.

        We strongly recommend letting WineSitting compute the earliest delivery date. If a date is supplied,
        preparation starts the same working day for orders created before 1pm and the next working day otherwise.
        Shipping then adds one day for departments 75, 92, 93, 94, 28, 45, 60, 77, 78, 91 and 95;
        three days in France up to 24 bottles; four days in France above 24 bottles; and six days for the rest of the world.
        Deliveries only occur on working days.

        Delivery time boundaries must be provided together and must span at least two hours within the same available slot group.
        An order cannot be edited after creation; delete it and create a new one instead.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderInput'
            example:
              content:
                - reference: 104278|0.75|bottles
                  quantity: 6
                - reference: 104278|1.5|CBO6
                  quantity: 6
              allow_partial_fulfillment: true
              pickup: false
              address:
                firstname: Test
                lastname: Test
                street1: 86 rue Baron le Roy
                city: Paris
                zipcode: '75012'
                country: FR
                phone: '+33123456789'
      responses:
        '200':
          description: Order created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
              example:
                id: 109956
                comment: null
                is_pickup: false
                address:
                  id: 245327
                  firstname: Test
                  lastname: Test
                  company: null
                  street1: 86 rue Baron le Roy
                  street2: null
                  city: Paris
                  zipcode: '75012'
                  country: FR
                  phone: '+33123456789'
                  email: null
                  vat_number: null
                  detail: null
                  reference: null
                pickup_warehouse: null
                warehouse:
                  id: 4
                  public_name: WineSitting Wissous B
                  street1: 14/16 bd Arago
                  street2: ''
                  city: Wissous
                  zipcode: '91320'
                  phone: '+33188400480'
                  email: contact@winesitting.com
                  country: FR
                  information: null
                  time_slots: null
                  excise_number: 'FR00000000000'
                attachments: []
                delivery_date:
                  date: '2024-08-20 00:00:00.000000'
                  timezone_type: 3
                  timezone: Europe/Paris
                delivery_start: '02:00'
                delivery_end: '04:00'
                preparation_date:
                  date: '2024-08-19 00:00:00.000000'
                  timezone_type: 3
                  timezone: Europe/Paris
                tracking_numbers: []
                carrier: WineSitting
                status:
                  value: 0
                  name: NEW
                  label: En attente de préparation
                additional_status: null
                reference: null
                origin: API
                created_by: Test Test
                updated_by: Test Test
                created_at:
                  date: '2024-07-15 15:30:20.000000'
                  timezone_type: 3
                  timezone: Europe/Paris
                updated_at:
                  date: '2024-08-19 12:43:49.000000'
                  timezone_type: 3
                  timezone: Europe/Paris
                content:
                  - id: 104278
                    wine: Chateau De Briace
                    color: Blanc
                    country: FR
                    region: Loire
                    appelation: Muscadet Sevre et Maine
                    description: null
                    ranking: null
                    vintage: 2022
                    alcohol: 13
                    format: 0.75
                    price: null
                    comment: null
                    quantity: 6
                    box: bottles
                    reference: 104278|0.75|bottles
                errors:
                  - reference: 104278|1.5|CBO6
                    ordered: 6
                    available: 3
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/{id}:
    parameters:
      - $ref: '#/components/parameters/OrderId'
    get:
      tags: [Orders]
      x-position: 2
      operationId: getOrder
      summary: Get a specific order
      responses:
        '200':
          description: Order returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags: [Orders]
      x-position: 4
      operationId: deleteOrder
      summary: Delete an order
      description: An order already in preparation cannot be deleted.
      responses:
        '200':
          description: Deleted order. Its identifier is null after deletion.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/delivery-cost:
    get:
      tags: [Orders]
      x-position: 5
      operationId: getOrderDeliveryCost
      summary: Get delivery cost
      description: Send a complete order payload to receive an accurate delivery cost.
      requestBody:
        $ref: '#/components/requestBodies/OrderCalculation'
      responses:
        '200':
          description: Delivery cost returned successfully.
          content:
            application/json:
              schema:
                type: object
                required: [cost]
                properties:
                  cost:
                    type: number
                    format: float
              example:
                cost: 10
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/time-slots:
    get:
      tags: [Orders]
      x-position: 6
      operationId: getOrderTimeSlots
      summary: Get time slots
      description: Send a complete order payload to receive the available delivery slots.
      requestBody:
        $ref: '#/components/requestBodies/OrderCalculation'
      responses:
        '200':
          description: Available delivery slots.
          content:
            application/json:
              schema:
                type: object
                required: [delivery_slots]
                properties:
                  delivery_slots:
                    type: array
                    items:
                      $ref: '#/components/schemas/DeliverySlot'
              example:
                delivery_slots:
                  - slot: '07:30 - 16:30'
                    group: Journée
                    start: '07:30:00'
                    end: '16:30:00'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/earliest-delivery-date:
    get:
      tags: [Orders]
      x-position: 7
      operationId: getOrderEarliestDeliveryDate
      summary: Get earliest delivery date
      description: Send an order payload to receive its earliest possible delivery date.
      requestBody:
        $ref: '#/components/requestBodies/OrderCalculation'
      responses:
        '200':
          description: Earliest delivery date returned successfully.
          content:
            application/json:
              schema:
                type: object
                required: [earliest_delivery_date]
                properties:
                  earliest_delivery_date:
                    type: string
                    format: date
              example:
                earliest_delivery_date: '2024-09-11'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/invalid-dates:
    get:
      tags: [Orders]
      x-position: 8
      operationId: getOrderInvalidDates
      summary: Get invalid dates
      description: Returns invalid delivery dates from the current year through the following year for the supplied order.
      requestBody:
        $ref: '#/components/requestBodies/OrderCalculation'
      responses:
        '200':
          description: Invalid delivery dates returned successfully.
          content:
            application/json:
              schema:
                type: object
                required: [invalid_dates]
                properties:
                  invalid_dates:
                    type: array
                    items:
                      type: string
                      format: date
              example:
                invalid_dates: ['2024-04-01', '2024-05-01']
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'

  /orders/{id}/files:
    parameters:
      - $ref: '#/components/parameters/OrderId'
    post:
      tags: [Orders]
      x-position: 9
      operationId: uploadOrderFiles
      summary: Send attachments
      description: |
        Upload one or more files while the order status is `NEW`.
        Accepted extensions are jpg, jpeg, png, gif, pdf, doc, docx, odt, rtf, xls, xlsx, ods, csv, txt, md, ppt, pptx and odp.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [files]
              properties:
                files:
                  type: array
                  items:
                    type: string
                    format: binary
      responses:
        '200':
          description: Order with the new attachments.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /orders/{id}/files/{attachment}:
    parameters:
      - $ref: '#/components/parameters/OrderId'
      - $ref: '#/components/parameters/AttachmentName'
    get:
      tags: [Orders]
      x-position: 10
      operationId: downloadOrderFile
      summary: Get an attachment
      responses:
        '200':
          description: Binary attachment.
          headers:
            Content-Disposition:
              description: Attachment filename.
              schema:
                type: string
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /putaways:
    get:
      tags: [Putaways]
      x-position: 1
      operationId: listPutaways
      summary: Get all putaways
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/OrderDirection'
      responses:
        '200':
          description: Putaways returned successfully.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/TotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Putaway'
              example:
                - id: 3
                  status:
                    value: 35
                    name: ACQUITTING
                    label: En cours d'acquittement
                  expeditor: null
                  reference: null
                  warehouse:
                    id: 4
                    public_name: WineSitting Wissous B
                    street1: 14/16 bd Arago
                    street2: ''
                    city: Wissous
                    zipcode: '91320'
                    phone: '+33188400480'
                    email: contact@winesitting.com
                    country: FR
                    information: null
                    time_slots: null
                    excise_number: 'FR00000000000'
                  validatedAt:
                    date: '2024-08-28 11:55:27.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  content:
                    - id: 93867
                      wine: Abbazia di Propezzano
                      color: Rouge
                      country: IT
                      region: Abruzzes
                      appelation: Montepulciano d'Abruzzo
                      description: null
                      ranking: null
                      vintage: 2020
                      alcohol: 13.5
                      format: 0.75
                      price: null
                      comment: null
                      quantity: 6
                      box: bottles
                      reference: 93867|0.75|bottles
                  attachments: []
        '401':
          $ref: '#/components/responses/Unauthorized'

  /putaways/{id}:
    parameters:
      - $ref: '#/components/parameters/PutawayId'
    get:
      tags: [Putaways]
      x-position: 2
      operationId: getPutaway
      summary: Get a specific putaway
      responses:
        '200':
          description: Putaway returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Putaway'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /putaways/{id}/files/{attachment}:
    parameters:
      - $ref: '#/components/parameters/PutawayId'
      - $ref: '#/components/parameters/AttachmentName'
    get:
      tags: [Putaways]
      x-position: 3
      operationId: downloadPutawayFile
      summary: Get an attachment
      responses:
        '200':
          description: Binary attachment.
          headers:
            Content-Disposition:
              description: Attachment filename.
              schema:
                type: string
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /invoices:
    get:
      tags: [Invoices]
      x-position: 1
      operationId: listInvoices
      summary: Get all invoices
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/OrderDirection'
      responses:
        '200':
          description: Invoices returned successfully.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/TotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Invoice'
              example:
                - id: 38756
                  state:
                    value: 4
                    name: PAID
                    label: Réglée
                  validated_at:
                    date: '2024-07-02 00:00:00.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  invoice_lines:
                    - id: 203646
                      label: null
                      quantity: 1
                      price: 12
                      vat: 20
                      product:
                        id: 138
                        name: Nom du produit
                        description: null
                  address:
                    id: 245324
                    firstname: Dupont1
                    lastname: Dupond1
                    company: Société XXX1
                    street1: 14 rue des Mésanges
                    street2: null
                    city: Paris
                    zipcode: '75005'
                    country: FR
                    phone: '+33234567891'
                    email: null
                    vat_number: '26375245'
                    detail: null
                    reference: null
                  invoicing_company: WineSitting
                  purchase_order: null
                  payment_mode: cb
                  created_at:
                    date: '2024-07-02 17:11:34.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  updated_at:
                    date: '2024-07-02 17:40:05.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
                  subTotal: 12
                  vat: 2.4
                  total: 14.4
        '401':
          $ref: '#/components/responses/Unauthorized'

  /invoices/{id}:
    parameters:
      - $ref: '#/components/parameters/InvoiceId'
    get:
      tags: [Invoices]
      x-position: 2
      operationId: getInvoice
      summary: Get a specific invoice
      responses:
        '200':
          description: Invoice returned successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Invoice'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'

  /transactions:
    get:
      tags: [Transactions]
      x-position: 1
      operationId: listTransactions
      summary: Get all transactions
      description: Retrieves cellar movements associated with the current account.
      parameters:
        - $ref: '#/components/parameters/Offset'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/OrderDirection'
        - name: type
          in: query
          description: Filter incoming or outgoing movements.
          schema:
            type: string
            enum: [in, out]
        - name: start_date
          in: query
          description: Inclusive start date.
          schema:
            type: string
            format: date
        - name: end_date
          in: query
          description: Inclusive end date. Must be later than `start_date`.
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Transactions returned successfully.
          headers:
            X-Total-Count:
              $ref: '#/components/headers/TotalCount'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Transaction'
              example:
                - id: 10257484
                  type: e
                  source: null
                  destination:
                    id: 59296
                    code: ARRIVEE-WISSOUS-B
                  comment: 'Sortie : 32588'
                  content:
                    - id: 104278
                      wine: Chateau De Briace
                      color: Blanc
                      country: FR
                      region: Loire
                      appelation: Muscadet Sevre et Maine
                      description: null
                      ranking: null
                      vintage: 2022
                      alcohol: 13
                      format: 1.5
                      price: null
                      comment: null
                      quantity: 1
                      box: CBO6
                      reference: 104278|1.5|CBO6
                  created_at:
                    date: '2024-07-15 15:29:27.000000'
                    timezone_type: 3
                    timezone: Europe/Paris
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationFailed'

  /enums:
    get:
      tags: [Enums]
      x-position: 1
      operationId: getEnumValues
      summary: Get enum values
      parameters:
        - name: label
          in: query
          required: true
          description: Enum to retrieve.
          schema:
            type: string
            enum:
              - cellar_preference
              - delivery_slip_preference
              - delivery_slip_sort_preference
              - email_preference
              - invoice_state
              - order_status
              - putaway_status
          example: order_status
      responses:
        '200':
          description: Enum values returned successfully.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EnumValue'
              example:
                - value: 0
                  name: NEW
                  label: En attente de préparation
                - value: 10
                  name: PROCESSING
                  label: En cours de préparation
                - value: 30
                  name: MERGING
                  label: En cours de fusion
                - value: 35
                  name: PROCESSED
                  label: En cours de validation
                - value: 40
                  name: READY
                  label: Prête
                - value: 50
                  name: SHIPPED
                  label: Expédiée
                - value: 60
                  name: DELIVERED
                  label: Livrée/Enlevée à l'entrepôt
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationFailed'

components:
  securitySchemes:
    WineSittingApiKey:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: Use your WineSitting API key as the Bearer token. Replace generated placeholders with `YourBearerToken`.
  headers:
    TotalCount:
      description: Total number of matching resources, independently from `offset` and `limit`.
      schema:
        type: integer
        minimum: 0
      example: 42
  parameters:
    Offset:
      name: offset
      in: query
      description: Requested offset.
      schema:
        type: integer
        minimum: 0
        default: 0
    Limit:
      name: limit
      in: query
      description: Number of resources to return. The maximum is 500.
      schema:
        type: integer
        minimum: 1
        maximum: 500
        default: 30
    OrderDirection:
      name: order
      in: query
      description: Sort direction.
      schema:
        type: string
        enum: [asc, desc]
        default: desc
    AddressId:
      name: id
      in: path
      required: true
      description: Address identifier.
      schema:
        type: integer
        minimum: 1
      example: 2531
    WarehouseId:
      name: id
      in: path
      required: true
      description: Warehouse identifier.
      schema:
        type: integer
        minimum: 1
      example: 4
    OrderId:
      name: id
      in: path
      required: true
      description: Order identifier.
      schema:
        type: integer
        minimum: 1
      example: 109930
    PutawayId:
      name: id
      in: path
      required: true
      description: Putaway identifier.
      schema:
        type: integer
        minimum: 1
      example: 3
    InvoiceId:
      name: id
      in: path
      required: true
      description: Invoice identifier.
      schema:
        type: integer
        minimum: 1
      example: 38756
    AttachmentName:
      name: attachment
      in: path
      required: true
      description: Attachment filename as returned in the resource attachments list.
      schema:
        type: string
      example: my-attachment.pdf
  requestBodies:
    OrderCalculation:
      required: true
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OrderInput'
          example:
            content:
              - reference: 104278|0.75|bottles
                quantity: 1
            pickup: true
  responses:
    BadRequest:
      description: Invalid JSON, content type, request payload or business rule.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unauthorized:
      description: The Bearer token is missing or invalid.
      content:
        application/json:
          schema:
            type: object
            required: [error]
            properties:
              error:
                type: string
          example:
            error: Invalid credentials.
    Forbidden:
      description: The authenticated account cannot access or modify this resource.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: Resource not found.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    ValidationFailed:
      description: Request payload or query string validation failed.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ValidationProblem'
  schemas:
    Account:
      type: object
      required:
        - firstname
        - lastname
        - email
        - warehouse
        - email_billing
        - email_stock
        - email_preference
        - cellar_preference
        - delivery_slip_preference
      properties:
        firstname:
          type: string
        lastname:
          type: string
        company:
          type: string
          nullable: true
        email:
          type: string
          format: email
        warehouse:
          $ref: '#/components/schemas/Warehouse'
        parent:
          type: object
          nullable: true
          additionalProperties: true
        email_billing:
          type: array
          items:
            type: string
            format: email
        email_stock:
          type: array
          items:
            type: string
            format: email
        minimum_stock:
          type: integer
          nullable: true
          minimum: 0
        email_preference:
          type: array
          description: Email notification types the account does not want to receive.
          items:
            type: string
        cellar_preference:
          type: array
          description: Columns hidden in the web cellar.
          items:
            type: string
        delivery_slip_preference:
          type: array
          description: Columns hidden on delivery slips.
          items:
            type: string
    AccountUpdate:
      type: object
      required: [firstname, lastname, email]
      properties:
        firstname:
          type: string
          maxLength: 255
        lastname:
          type: string
          maxLength: 255
        company:
          type: string
          nullable: true
          maxLength: 255
        minimum_stock:
          type: integer
          nullable: true
          minimum: 0
        email:
          type: string
          format: email
          maxLength: 180
        email_billing:
          type: array
          nullable: true
          items:
            type: string
            format: email
            maxLength: 255
        email_stock:
          type: array
          nullable: true
          items:
            type: string
            format: email
            maxLength: 255
        email_preference:
          type: array
          default: []
          items:
            type: string
        cellar_preference:
          type: array
          default: []
          items:
            type: string
        delivery_slip_preference:
          type: array
          default: []
          items:
            type: string
        delivery_slip_sort_preference:
          type: string
          enum: [default, wine, country, region, appelation, vintage]
          default: default
    AddressInput:
      type: object
      required: [street1, city, zipcode, country, phone]
      description: Either `company`, or both `firstname` and `lastname`, must be provided.
      properties:
        firstname:
          type: string
          nullable: true
          maxLength: 255
        lastname:
          type: string
          nullable: true
          maxLength: 255
        company:
          type: string
          nullable: true
          maxLength: 255
        vatNumber:
          type: string
          nullable: true
          maxLength: 255
          description: VAT number. Request DTO field name; responses expose `vat_number`.
        street1:
          type: string
          maxLength: 255
        street2:
          type: string
          nullable: true
          maxLength: 255
        city:
          type: string
          maxLength: 255
        zipcode:
          type: string
          maxLength: 255
        country:
          type: string
          minLength: 2
          maxLength: 2
          description: ISO 3166-1 alpha-2 country code.
          example: FR
        phone:
          type: string
          maxLength: 255
          description: E.164 phone number.
          example: '+33123456789'
        email:
          type: string
          nullable: true
          format: email
          maxLength: 255
        detail:
          type: string
          nullable: true
          description: Address details, excluding delivery time slots.
        reference:
          type: string
          nullable: true
          maxLength: 255
    Address:
      type: object
      required: [id, street1, city, zipcode, country, phone]
      properties:
        id:
          type: integer
          nullable: true
        firstname:
          type: string
          nullable: true
        lastname:
          type: string
          nullable: true
        company:
          type: string
          nullable: true
        street1:
          type: string
        street2:
          type: string
          nullable: true
        city:
          type: string
        zipcode:
          type: string
        country:
          type: string
        phone:
          type: string
        email:
          type: string
          nullable: true
          format: email
        vat_number:
          type: string
          nullable: true
        detail:
          type: string
          nullable: true
        reference:
          type: string
          nullable: true
    BottleReference:
      type: object
      required: [id, wine, color, country, region, appelation, format, quantity, box, reference]
      properties:
        id:
          type: integer
        wine:
          type: string
        color:
          type: string
        country:
          type: string
        region:
          type: string
        appelation:
          type: string
          description: Appellation name. The response field keeps this historical spelling.
        description:
          type: string
          nullable: true
        ranking:
          type: string
          nullable: true
        vintage:
          type: integer
          nullable: true
        alcohol:
          type: number
          format: float
          nullable: true
        format:
          type: number
          format: float
        price:
          type: number
          format: float
          nullable: true
        comment:
          type: string
          nullable: true
        quantity:
          type: integer
          minimum: 1
        box:
          type: string
        reference:
          type: string
          pattern: '^\d+\|\d+(\.\d+)?\|.+$'
        external_reference:
          type: string
          nullable: true
          description: Present only for catalog-enabled accounts.
    OrderInput:
      type: object
      required: [content]
      properties:
        delivery_date:
          type: string
          format: date
          nullable: true
          description: Must not be earlier than today. WineSitting chooses the earliest possible date when omitted.
        delivery_start:
          type: string
          nullable: true
          pattern: '^([01]\d|2[0-3]):[0-5]\d$'
          description: Must be provided together with `delivery_end`.
          example: '07:30'
        delivery_end:
          type: string
          nullable: true
          pattern: '^([01]\d|2[0-3]):[0-5]\d$'
          description: Must be provided together with `delivery_start`.
          example: '16:30'
        pickup:
          type: boolean
          default: true
          description: When false, `address` is required. An address may also be supplied for pickup customs documents.
        comment:
          type: string
          nullable: true
        reference:
          type: string
          nullable: true
          maxLength: 255
        allow_partial_fulfillment:
          type: boolean
          default: false
          description: Select the maximum available quantities instead of failing the whole order.
        address:
          allOf:
            - $ref: '#/components/schemas/AddressInput'
          nullable: true
        content:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/OrderLineInput'
    OrderLineInput:
      type: object
      required: [reference, quantity]
      properties:
        reference:
          type: string
          maxLength: 255
          pattern: '^\d+\|\d+(\.\d+)?\|.+$'
          example: 104278|0.75|bottles
        quantity:
          type: integer
          minimum: 1
          example: 6
    Order:
      type: object
      required: [id, is_pickup, attachments, tracking_numbers, status, content]
      properties:
        id:
          type: integer
          nullable: true
        comment:
          type: string
          nullable: true
        is_pickup:
          type: boolean
        address:
          allOf:
            - $ref: '#/components/schemas/Address'
          nullable: true
        pickup_warehouse:
          allOf:
            - $ref: '#/components/schemas/Warehouse'
          nullable: true
        warehouse:
          allOf:
            - $ref: '#/components/schemas/Warehouse'
          nullable: true
        attachments:
          type: array
          items:
            type: object
            additionalProperties: true
        delivery_date:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        delivery_start:
          type: string
          nullable: true
          example: '07:30'
        delivery_end:
          type: string
          nullable: true
          example: '16:30'
        preparation_date:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        tracking_numbers:
          type: array
          items:
            type: string
        carrier:
          type: string
        status:
          $ref: '#/components/schemas/StatusValue'
        additional_status:
          nullable: true
        reference:
          type: string
          nullable: true
        origin:
          type: string
          nullable: true
        created_by:
          type: string
          nullable: true
        updated_by:
          type: string
          nullable: true
        created_at:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        updated_at:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        content:
          type: array
          items:
            $ref: '#/components/schemas/BottleReference'
        errors:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/PartialFulfillmentError'
    PartialFulfillmentError:
      type: object
      required: [reference, ordered, available]
      properties:
        reference:
          type: string
        ordered:
          type: integer
        available:
          type: integer
    DeliverySlot:
      type: object
      required: [slot, group, start, end]
      properties:
        slot:
          type: string
        group:
          type: string
        start:
          type: string
          example: '07:30:00'
        end:
          type: string
          example: '16:30:00'
    Putaway:
      type: object
      required: [id, status, content, attachments]
      properties:
        id:
          type: integer
        status:
          $ref: '#/components/schemas/StatusValue'
        expeditor:
          type: string
          nullable: true
        reference:
          type: string
          nullable: true
        warehouse:
          allOf:
            - $ref: '#/components/schemas/Warehouse'
          nullable: true
        validatedAt:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        content:
          type: array
          items:
            $ref: '#/components/schemas/BottleReference'
        attachments:
          type: array
          items:
            type: object
            additionalProperties: true
    Invoice:
      type: object
      required: [id, state, invoice_lines, invoicing_company, subTotal, vat, total]
      properties:
        id:
          type: integer
        state:
          $ref: '#/components/schemas/StatusValue'
        validated_at:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        invoice_lines:
          type: array
          items:
            $ref: '#/components/schemas/InvoiceLine'
        address:
          allOf:
            - $ref: '#/components/schemas/Address'
          nullable: true
        invoicing_company:
          type: string
        purchase_order:
          type: string
          nullable: true
        payment_mode:
          type: string
          nullable: true
        created_at:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        updated_at:
          allOf:
            - $ref: '#/components/schemas/ApiDateTime'
          nullable: true
        subTotal:
          type: number
          format: float
        vat:
          type: number
          format: float
        total:
          type: number
          format: float
    InvoiceLine:
      type: object
      required: [id, quantity, price, vat]
      properties:
        id:
          type: integer
        label:
          type: string
          nullable: true
        quantity:
          type: number
          format: float
        price:
          type: number
          format: float
        vat:
          type: number
          format: float
        product:
          allOf:
            - $ref: '#/components/schemas/Product'
          nullable: true
    Product:
      type: object
      required: [id, name]
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
          nullable: true
    Transaction:
      type: object
      required: [id, type, content, created_at]
      properties:
        id:
          type: integer
        type:
          type: string
          enum: [e, s]
          description: Serialized transaction type, where `e` is incoming and `s` is outgoing.
        source:
          allOf:
            - $ref: '#/components/schemas/Location'
          nullable: true
        destination:
          allOf:
            - $ref: '#/components/schemas/Location'
          nullable: true
        comment:
          type: string
          nullable: true
        content:
          type: array
          items:
            $ref: '#/components/schemas/BottleReference'
        created_at:
          $ref: '#/components/schemas/ApiDateTime'
    Location:
      type: object
      required: [id, code]
      properties:
        id:
          type: integer
        code:
          type: string
    Warehouse:
      type: object
      required: [id, public_name, street1, city, zipcode, country]
      properties:
        id:
          type: integer
        public_name:
          type: string
        street1:
          type: string
        street2:
          type: string
          nullable: true
        city:
          type: string
        zipcode:
          type: string
        phone:
          type: string
          nullable: true
        email:
          type: string
          nullable: true
          format: email
        country:
          type: string
        information:
          nullable: true
        time_slots:
          nullable: true
        excise_number:
          type: string
    StatusValue:
      type: object
      required: [value, name, label]
      properties:
        value:
          type: integer
        name:
          type: string
        label:
          type: string
    EnumValue:
      type: object
      required: [value, label]
      properties:
        value:
          oneOf:
            - type: integer
            - type: string
        name:
          type: string
          nullable: true
        label:
          type: string
    ApiDateTime:
      type: object
      required: [date, timezone_type, timezone]
      properties:
        date:
          type: string
          example: '2024-09-11 00:00:00.000000'
        timezone_type:
          type: integer
          example: 3
        timezone:
          type: string
          example: Europe/Paris
    Problem:
      type: object
      properties:
        type:
          type: string
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
    ValidationProblem:
      allOf:
        - $ref: '#/components/schemas/Problem'
        - type: object
          properties:
            violations:
              type: array
              items:
                type: object
                properties:
                  propertyPath:
                    type: string
                  title:
                    type: string
