# Generated by tools/connector-api-spec.php. Do not edit.
openapi: '3.1.0'
info:
  title: HikaShop Connector API
  version: '6.6.0'
  description: |-
    The connector plugin ships inside HikaShop Business and answers JSON over HTTPS. The mobile
    application is one client of it; anything it does, your own code can do.
    
    Every response carries the same envelope, `{"data": …, "meta": …, "error": null}`, and every route
    except `POST /pair` carries a device token as `Authorization: Bearer <token>`. A token holds the
    scopes `read` and optionally `write`, and the operator it is bound to keeps their own access
    levels, so a device may hold `write` and still be refused an order its operator cannot see.
    
    The base path is a plugin parameter and a shop can move it. Nothing else here changes between
    shops.
    
    ## Pairing a device
    
    A client never sees the merchant's password. It is given a **pairing code** instead, generated in
    **System > App Devices** of the backend, and exchanges it once for a token of its own.
    
    The code is nine characters, lives five minutes, may be tried five times before it dies, and can
    only be spent once. `POST /pair` is rate limited to thirty attempts per minute per address, which
    is the only route that answers without a token.
    
    What comes back is the token, the scopes the code granted, and the id of the device row. The token
    is shown once and stored only as a SHA-256 hash, so a device that loses it pairs again rather than
    recovering it. Revoking a device is deleting its row in that same screen, and every token it held
    stops working at once.
    
    ```
    curl -X POST "$SHOP/hikashop-api/v1/pair" \
      -H "Content-Type: application/json" \
      -d '{ "code": "K7QM-2F84", "device_name": "Counter tablet" }'
    
    # then, for everything else
    curl "$SHOP/hikashop-api/v1/products" \
      -H "Authorization: Bearer $TOKEN"
    ```
    
    ## Scopes and access levels
    
    Two separate checks run on every request, and the narrower one wins.
    
    The **scope** is what the device may do at all: `read`, and `write` if the pairing code granted it.
    A device without `write` is refused every route that changes anything, whoever holds it.
    
    The **access levels** are the shop's own, belonging to the operator the device is bound to. They
    decide which records that person may see and change, exactly as they do in the backend. So a device
    holding `write` can still be refused an order, and a device holding only `read` sees a smaller
    catalogue than another one.
    
    This is why an integration should be given its own device rather than borrowing one: revoke it and
    nothing else is disturbed, and its operator's access levels are the ceiling on what it can reach.
    
    ```
    {
      "data": null,
      "error": {
        "code": "forbidden",
        "message": "This device does not have the required scope."
      }
    }
    ```
    
    ## The envelope, errors and paging
    
    Every JSON answer has the same three keys. `data` is the payload, `meta` carries paging and other
    context when there is any and is `null` otherwise, and `error` is `null` on success.
    
    On failure `data` is `null` and `error` holds a stable `code` and a human `message`. Read the code,
    not the message: the message is written for a person and may be translated or reworded, the code is
    what your own logic should branch on.
    
    Listings page with `start` and `limit` in the query, and answer with `start`, `limit` and `total` in
    the envelope's `meta`. `total` counts the rows matching the filter before paging, so it is how you
    know there is another page. `limit` is capped, usually at a hundred, and asking for more silently
    gets you the cap rather than an error.
    
    One thing to note about a listing: `data` is the list itself, not an object wrapping it. The paging
    lives in `meta`.
    
    - `invalid_request` (400) A required field is missing or malformed.
    - `unauthorized` (401) No token, or a token that is unknown, revoked or unpublished.
    - `forbidden` (403) The device lacks the scope, or the operator lacks the access level.
    - `not_found` (404) No such record, or no such route. Also returned when the operator may not see the record, so that it cannot be probed for.
    - `too_many_requests` (429) Rate limited. Only /pair does this.
    
    ```
    {
      "data": [ … ],
      "meta": { "start": 0, "limit": 20, "total": 307 },
      "error": null
    }
    ```
    
    ## Calling it from a browser
    
    The API answers cross-origin requests, because the application is a web application as well as a
    phone one. `Access-Control-Allow-Origin` is `*`, the allowed methods are GET, POST, PUT, DELETE and
    OPTIONS, and the allowed headers are `Authorization`, `Content-Type` and `X-Hikashop-Token`. A
    preflight is answered immediately and may be cached for a day.
    
    `*` with a bearer token is deliberate and safe in the way cookies would not be: nothing is sent
    automatically by the browser, so a page on another origin can only call this API if it already
    holds a token, and a token is only ever obtained by pairing.
    
    Send the token in the `Authorization` header. Never put it in the query string, where it would be
    written to every access log between you and the shop.
    
    ```
    OPTIONS /hikashop-api/v1/products
    
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
    Access-Control-Allow-Headers: Authorization, Content-Type, X-Hikashop-Token
    Access-Control-Max-Age: 86400
    ```
    
    ## Adding your own routes
    
    A HikaShop plugin can serve paths of its own through the same base path, the same envelope and the
    same authentication, by listening to three events.
    
    `onConnectorBeforeRoute` fires before any matching, so it can intercept a path the connector also
    serves. `onConnectorRoute` fires only when nothing matched, which is where a new path belongs.
    `onConnectorBeforeResponse` fires just before the JSON is written, for adding a field to an answer
    somebody else built.
    
    The listener receives one object by reference, carrying `path` (the part after the base path),
    `method`, `base_path`, `response`, and `handled`. Authenticate with `requireScope()` exactly as the
    connector's own routes do, answer through `$ctx->response`, and set `$ctx->handled = true` so the
    router stops rather than falling through to its 404.
    
    ```
    public function onConnectorRoute(&$ctx) {
        if ($ctx->path !== 'warehouse/stock' || $ctx->method !== 'GET')
            return;
    
        $device = HikashopConnectorAuth::requireScope($ctx->response, 'read');
        if ($device === null)
            return;          // it has already answered 401 or 403
    
        $ctx->response->data(array('pallets' => $this->countPallets()));
        $ctx->handled = true;
    }
    ```
  license:
    name: 'Proprietary, ships with HikaShop Business'
servers:
  - url: 'https://your-shop.tld/hikashop-api/v1'
    description: The base path is a plugin parameter and a shop can move it.
components:
  securitySchemes:
    deviceToken:
      type: http
      scheme: bearer
      description: The token returned once by POST /pair.
  schemas:
    Error:
      type: object
      properties:
        data:
          type: 'null'
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
security:
  - deviceToken: []
paths:
  '/site':
    get:
      operationId: get-site
      summary: What this shop is
      description: |-
        The first call a client makes. It says what it has connected to, who it is connected as, and what
        that operator is allowed to do, which is enough to draw an interface without asking for anything
        it will only be refused.
        
        Nothing here changes often. Cache it, and use `GET /version` to know when to look again.
      tags:
        - site
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      app:
                        type: string
                        description: 'Always `hikashop-connector`. A cheap way to be sure you are talking to this API and not to something else answering on that path.'
                      api_version:
                        type: string
                        description: 'Semver of the API itself, not of HikaShop. A minor bump adds fields or routes, a major one breaks something.'
                      site_name:
                        type: string
                        description: 'What the site calls itself, which is what a merchant recognises the shop by.'
                      hikashop_version:
                        type:
                          - string
                          - 'null'
                        description: The HikaShop release running there.
                      cms:
                        type: object
                        properties:
                          name:
                            type: boolean
                            description: '`joomla` or `wordpress`.'
                          version:
                            type:
                              - string
                              - 'null'
                            description: The CMS release.
                        description: What it is running on.
                      edition:
                        type: string
                        description: '`starter`, `essential` or `business`. This API only answers on Business, so in practice it is `business` unless the licence has lapsed.'
                      logo:
                        type: string
                        description: 'The shop''s logo as an absolute URL, empty when none is set. The same setting the invoices use.'
                      currency:
                        type: object
                        properties:
                          default:
                            type: integer
                            description: 'The currency id, a row in the shop''s own table, not an ISO code.'
                        description: 'The shop''s money.'
                      price_with_tax:
                        type: boolean
                        description: 'Whether prices are shown to customers with tax included. Display only: the prices in this API are as stored.'
                      operator:
                        type: object
                        properties:
                          id:
                            type: integer
                            description: '`0` when the device is bound to nobody, which is a device paired without an operator.'
                          name:
                            type:
                              - string
                              - 'null'
                            description: Their display name.
                          role:
                            type: string
                            description: '`admin` when they may manage the component, `staff` otherwise.'
                        description: The user the device is bound to.
                      scopes:
                        type: array
                        items:
                          type: string
                        description: 'What this device may do: `read`, and `write` when it was granted.'
                      permissions:
                        type: object
                        description: 'What the operator''s access levels allow, keyed by resource (`product`, `order`, `category`, `discount`, `user`, `zone`, `characteristic`, `massaction`, `dashboard`), each with `view`, `manage` and `delete`. Use it to leave controls out rather than showing a screen full of refusals. Its keys are the resources this HikaShop knows about, so an add-on can add one.'
                        additionalProperties: true
                        x-open: true
                      capabilities:
                        type: object
                        properties:
                          pos:
                            type: boolean
                            description: 'Reserved for the point of sale, false for now.'
                          push:
                            type: boolean
                            description: 'Reserved for push notifications, false for now.'
                          multivendor:
                            type: boolean
                            description: Whether HikaMarket is installed.
                        description: What this shop can do beyond the basics.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/settings':
    get:
      operationId: get-settings
      summary: The settings that decide how prices read
      description: |-
        The handful of configuration flags a client needs in order to show the same figures the shop's own
        pages show. They are read only here; they are changed in the backend.
      tags:
        - settings
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      product_contact:
                        type: boolean
                        description: Whether products can be enquired about rather than bought.
                      product_waitlist:
                        type: boolean
                        description: Whether customers can join a waiting list for something out of stock.
                      price_with_tax:
                        type: boolean
                        description: Show prices with tax included.
                      floating_tax_prices:
                        type: boolean
                        description: 'Whether the tax shown depends on the customer, which means a price cannot be cached across customers.'
                      show_original_price:
                        type: boolean
                        description: Show the price before a discount alongside the discounted one.
                      round_calculations:
                        type: integer
                        description: When rounding happens during a calculation. Match it or your totals will differ from the shop by a cent.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/version':
    get:
      operationId: get-version
      summary: Change tokens for the cacheable resources
      description: |-
        Three opaque tokens, one for each resource worth caching. A token changes when its resource does,
        so a client keeps the copy it has until the token moves.
        
        Compare them, do not read them. They are strings today and their meaning is deliberately not part
        of this contract.
        
        This is what makes a dictionary of several thousand strings affordable: fetch it once, ask this
        cheap route afterwards.
      tags:
        - version
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      i18n:
                        type: string
                        description: 'Moves when the shop''s translations change.'
                      statuses:
                        type: string
                        description: 'Moves when an order status is added, renamed or unpublished.'
                      languages:
                        type: string
                        description: 'Moves when the shop''s languages change.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/statuses':
    get:
      operationId: get-statuses
      summary: 'The shop''s order statuses'
      description: |-
        Every published order status, in the order the backend shows them. A shop invents its own, so this
        list is not a fixed set and should never be hard coded.
        
        The `namekey` is the identifier to send to `POST /orders/{id}/status`; the `name` is for a person
        to read.
      tags:
        - statuses
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        namekey:
                          type: string
                          description: The identifier. This is what you send when changing an order.
                        name:
                          type: string
                          description: 'Translated into the operator''s language, ready to display.'
                        label_key:
                          type: string
                          description: 'The translation key behind that name, if you would rather translate it yourself.'
                        color:
                          type: string
                          description: 'The colour the backend uses for this status, empty when none is set.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/i18n':
    get:
      operationId: get-i18n
      summary: 'HikaShop''s own translation of a locale'
      description: |-
        The shop's translation dictionary for one locale, so that a client can name things exactly as the
        merchant's own site names them, including whatever they have overridden.
        
        It is large. Cache it against the `i18n` token from `GET /version` rather than fetching it again.
      tags:
        - i18n
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: locale
          in: query
          required: false
          description: 'A HikaShop language tag such as `en-GB`. Falls back to the site language when it is not installed.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      locale:
                        type: string
                        description: 'The locale actually served, which may not be the one asked for.'
                      strings:
                        type: object
                        description: 'Translation key to translated text. Thousands of keys, and a shop can add or override any of them, so there is no list to give.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/languages':
    get:
      operationId: get-languages
      summary: 'The shop''s languages'
      description: |-
        Which languages the shop publishes, and whether content translation is switched on at all.
        
        When `enabled` is false the shop is single-language and the translation routes have nothing to do.
        The default language is marked and comes last, because it is the one already being edited on the
        main form rather than a translation of it.
      tags:
        - languages
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                        description: Whether this shop translates its content. False on a single-language site.
                      languages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The language id in the shop''s table.'
                            code:
                              type: string
                              description: 'The tag, such as `fr-FR`.'
                            shortcode:
                              type: string
                              description: 'The lower-case underscored form, such as `fr_fr`, which is what the translation tables key on.'
                            site_default:
                              type: boolean
                              description: 'True for the language the shop is written in. Its text is the original, not a translation.'
                        description: The published languages.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/products/{id}':
    get:
      operationId: get-products-id
      summary: Read one product
      description: |-
        Everything the product editor needs in one call: the record, its prices, its images and files, its
        categories, its characteristics and variants, and the definitions of the custom fields that apply
        to it.
        
        Ask for a variant's id and you get the variant, with `parent_id` set. A parent's `variants` are
        listed in full, so you rarely need to.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: A parent product or a variant.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      code:
                        type: string
                        description: The SKU. Unique within the shop.
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      description_type:
                        type: string
                        description: Which editor the description was written with.
                      published:
                        type: boolean
                      quantity:
                        type: integer
                        description: '`-1` when this product does not track stock, which is not the same as `0`.'
                      msrp:
                        type: number
                        description: 'The manufacturer''s suggested price, shown struck through when the shop is configured to.'
                      gtin:
                        type: string
                        description: 'The barcode: EAN, UPC or ISBN. This is what `GET /products/lookup` matches on.'
                      condition:
                        type: string
                        description: 'New, used, refurbished. Used by the feeds rather than by the shop itself.'
                      weight:
                        type: number
                        description: 'Shipping weight, in `weight_unit`.'
                      weight_unit:
                        type: string
                        description: '`kg`, `g`, `lb` or `oz`.'
                      width:
                        type: number
                        description: 'In `dimension_unit`.'
                      height:
                        type: number
                        description: 'In `dimension_unit`.'
                      length:
                        type: number
                        description: 'In `dimension_unit`.'
                      dimension_unit:
                        type: string
                        description: '`m`, `cm`, `mm`, `ft` or `in`.'
                      min_per_order:
                        type: integer
                        description: 'The smallest quantity a customer may order, `0` for no minimum.'
                      max_per_order:
                        type: integer
                        description: 'The largest, `0` for no maximum.'
                      sale_start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which the product is not on sale.
                      sale_end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it is no longer sold.
                      page_title:
                        type: string
                        description: 'SEO title, empty to use the name.'
                      meta_description:
                        type: string
                        description: SEO description.
                      keywords:
                        type: string
                        description: SEO keywords.
                      canonical:
                        type: string
                        description: 'A canonical URL, when this page should point at another.'
                      url:
                        type: string
                        description: The address of the product page on the shop.
                      alias:
                        type: string
                        description: The slug used in that address.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User group ids, meaningful only when the mode is `groups`.'
                        description: 'Who may see the product: `mode` (`all`, `none` or `groups`) and `groups`, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                      contact:
                        type: boolean
                        description: Whether this product is enquired about rather than bought.
                      warehouse_id:
                        type: integer
                        description: 'The warehouse holding the stock, `0` when the shop has none.'
                      type:
                        type: string
                        description: '`main` for a product, `variant` for one of its variants.'
                      parent_id:
                        type: integer
                        description: 'The parent product when this is a variant, `0` otherwise.'
                      value_ids:
                        type: array
                        items:
                          type: integer
                        description: 'For a variant, the characteristic values it stands for.'
                      manufacturer_id:
                        type: integer
                        description: 'The brand, `0` when unset.'
                      manufacturer_name:
                        type: string
                        description: 'Its name, saving a second call.'
                      tax_id:
                        type: integer
                        description: 'The tax category, `0` when the product is untaxed.'
                      tax_name:
                        type: string
                        description: Its name.
                      tax_rate:
                        type: number
                        description: 'The rate as a fraction, so `0.2` is twenty percent.'
                      prices:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            value:
                              type: number
                              description: 'Tax excluded, as stored.'
                            currency_id:
                              type: integer
                            min_quantity:
                              type: integer
                              description: 'From how many items this row applies, which is how quantity breaks are expressed.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: 'Who this price is for, in the same shape as the product access.'
                            users:
                              type: array
                              items:
                                type: integer
                              description: 'Named customers, empty for everyone.'
                            zone_ids:
                              type: array
                              items:
                                type: integer
                              description: 'Zones this price applies in, empty for everywhere.'
                            start_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                            end_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                        description: 'Every price row, including the restricted ones. A product with none is not sellable.'
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: integer
                            description:
                              type: string
                              description: The alt text.
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: Who may see it.
                            free_download:
                              type: boolean
                              description: 'Files only; meaningless on an image.'
                        description: 'In the order the editor shows them; the first is the main image.'
                      files:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: Absolute.
                            ordering:
                              type: integer
                            description:
                              type: string
                            access:
                              type: object
                              description: Who may download it.
                            free_download:
                              type: boolean
                              description: Whether it can be downloaded without buying the product.
                        description: 'Downloadable files, in the same shape as the images.'
                      categories:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                        description: The categories the product is in.
                      bundle:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: integer
                              description: How many of it the bundle contains.
                        description: 'The products this one is made of, when it is a bundle.'
                      options:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: integer
                        description: Products offered as options alongside this one.
                      related:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: integer
                        description: Products shown as related.
                      tags:
                        type: array
                        items:
                          type: integer
                        description: CMS tag ids.
                      characteristics:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The characteristic, such as Size.'
                            name:
                              type: string
                              description: Its name.
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                    description: 'The value id, which is what a variant refers to.'
                                  value:
                                    type: string
                                    description: 'Its name, such as `M`.'
                              description: 'The values of it this product uses, such as S, M and L.'
                        description: The characteristics this product varies on. Empty when it has no variants.
                      variants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The variant is a product in its own right, and this is its id.'
                            code:
                              type: string
                              description: Its own SKU.
                            quantity:
                              type: integer
                              description: 'Its own stock. This is the figure to change, not the parent''s.'
                            published:
                              type: boolean
                            price:
                              type:
                                - number
                                - 'null'
                              description: '`null` when the variant has no price of its own and the parent''s applies.'
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  option_id:
                                    type: integer
                                    description: The characteristic.
                                  option_name:
                                    type: string
                                    description: 'Its name, so the variant can be labelled without a second call.'
                                  value_id:
                                    type: integer
                                    description: The value.
                                  value:
                                    type: string
                                    description: Its name.
                              description: 'Which characteristic values this variant stands for, one per characteristic.'
                            images:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                  name:
                                    type: string
                                  path:
                                    type: string
                                    description: Relative to the upload folder.
                                  url:
                                    type: string
                                    description: Absolute.
                                  ordering:
                                    type: integer
                              description: 'The variant''s own images, in the same shape as the product''s.'
                        description: 'Every variant, with its own code, stock, price and images. Empty for a product that does not vary.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: 'The key used in `custom_fields`.'
                            type:
                              type: string
                              description: '`text`, `radio`, `singledropdown`, `file`, and the rest of HikaShop''s field types.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type, before it is mapped to something a client can render.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save the product without it.
                            options:
                              type: array
                              items:
                                type: object
                              description: 'The choices, for a field that has them. Empty for a free text one.'
                            multiple:
                              type: boolean
                              description: Whether more than one choice may be selected.
                            translatable:
                              type: boolean
                              description: 'Whether its value can be translated, which is what the translation routes offer.'
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts, comma separated. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The definitions of the custom fields that apply to this product, so a client can build a form for them.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys are whatever this shop has configured; `fields` in the same response says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For custom fields holding a file, the file behind each value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
    put:
      operationId: put-products-id
      summary: Update a product
      description: |-
        Changes the fields you send and leaves the rest alone, which is the opposite of the price and
        category calls: this one merges.
        
        Sending nothing to change is refused rather than treated as a success, so a client cannot believe
        it saved something it did not.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product or variant.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: |-
                The same fields as `POST /products`, all of them optional here. A field you do not send keeps its
                value; `access` and `custom_fields` are accepted too.
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      code:
                        type: string
                        description: The SKU. Unique within the shop.
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      description_type:
                        type: string
                        description: Which editor the description was written with.
                      published:
                        type: boolean
                      quantity:
                        type: integer
                        description: '`-1` when this product does not track stock, which is not the same as `0`.'
                      msrp:
                        type: integer
                        description: 'The manufacturer''s suggested price, shown struck through when the shop is configured to.'
                      gtin:
                        type: string
                        description: 'The barcode: EAN, UPC or ISBN. This is what `GET /products/lookup` matches on.'
                      condition:
                        type: string
                        description: 'New, used, refurbished. Used by the feeds rather than by the shop itself.'
                      weight:
                        type: number
                        description: 'Shipping weight, in `weight_unit`.'
                      weight_unit:
                        type: string
                        description: '`kg`, `g`, `lb` or `oz`.'
                      width:
                        type: integer
                        description: 'In `dimension_unit`.'
                      height:
                        type: integer
                        description: 'In `dimension_unit`.'
                      length:
                        type: integer
                        description: 'In `dimension_unit`.'
                      dimension_unit:
                        type: string
                        description: '`m`, `cm`, `mm`, `ft` or `in`.'
                      min_per_order:
                        type: integer
                        description: 'The smallest quantity a customer may order, `0` for no minimum.'
                      max_per_order:
                        type: integer
                        description: 'The largest, `0` for no maximum.'
                      sale_start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which the product is not on sale.
                      sale_end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it is no longer sold.
                      page_title:
                        type: string
                        description: 'SEO title, empty to use the name.'
                      meta_description:
                        type: string
                        description: SEO description.
                      keywords:
                        type: string
                        description: SEO keywords.
                      canonical:
                        type: string
                        description: 'A canonical URL, when this page should point at another.'
                      url:
                        type: string
                        description: The address of the product page on the shop.
                      alias:
                        type: string
                        description: The slug used in that address.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User group ids, meaningful only when the mode is `groups`.'
                        description: 'Who may see the product: `mode` (`all`, `none` or `groups`) and `groups`, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                      contact:
                        type: boolean
                        description: Whether this product is enquired about rather than bought.
                      warehouse_id:
                        type: integer
                        description: 'The warehouse holding the stock, `0` when the shop has none.'
                      type:
                        type: string
                        description: '`main` for a product, `variant` for one of its variants.'
                      parent_id:
                        type: integer
                        description: 'The parent product when this is a variant, `0` otherwise.'
                      value_ids:
                        type: array
                        items:
                          type: integer
                        description: 'For a variant, the characteristic values it stands for.'
                      manufacturer_id:
                        type: integer
                        description: 'The brand, `0` when unset.'
                      manufacturer_name:
                        type: string
                        description: 'Its name, saving a second call.'
                      tax_id:
                        type: integer
                        description: 'The tax category, `0` when the product is untaxed.'
                      tax_name:
                        type: string
                        description: Its name.
                      tax_rate:
                        type: number
                        description: 'The rate as a fraction, so `0.2` is twenty percent.'
                      prices:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            value:
                              type: number
                              description: 'Tax excluded, as stored.'
                            currency_id:
                              type: integer
                            min_quantity:
                              type: integer
                              description: 'From how many items this row applies, which is how quantity breaks are expressed.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: 'Who this price is for, in the same shape as the product access.'
                            users:
                              type: array
                              items:
                                type: integer
                              description: 'Named customers, empty for everyone.'
                            zone_ids:
                              type: array
                              items:
                                type: integer
                              description: 'Zones this price applies in, empty for everywhere.'
                            start_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                            end_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                        description: 'Every price row, including the restricted ones. A product with none is not sellable.'
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: integer
                            description:
                              type: string
                              description: The alt text.
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: Who may see it.
                            free_download:
                              type: boolean
                              description: 'Files only; meaningless on an image.'
                        description: 'In the order the editor shows them; the first is the main image.'
                      files:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: Absolute.
                            ordering:
                              type: string
                            description:
                              type: string
                            access:
                              type: object
                              description: Who may download it.
                            free_download:
                              type: string
                              description: Whether it can be downloaded without buying the product.
                        description: 'Downloadable files, in the same shape as the images.'
                      categories:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                        description: The categories the product is in.
                      bundle:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                              description: How many of it the bundle contains.
                        description: 'The products this one is made of, when it is a bundle.'
                      options:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products offered as options alongside this one.
                      related:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products shown as related.
                      tags:
                        type: array
                        items:
                          type: integer
                        description: CMS tag ids.
                      characteristics:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The characteristic, such as Size.'
                            name:
                              type: string
                              description: Its name.
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                    description: 'The value id, which is what a variant refers to.'
                                  value:
                                    type: string
                                    description: 'Its name, such as `M`.'
                              description: 'The values of it this product uses, such as S, M and L.'
                        description: The characteristics this product varies on. Empty when it has no variants.
                      variants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The variant is a product in its own right, and this is its id.'
                            code:
                              type: string
                              description: Its own SKU.
                            quantity:
                              type: integer
                              description: 'Its own stock. This is the figure to change, not the parent''s.'
                            published:
                              type: boolean
                            price:
                              type:
                                - number
                                - 'null'
                              description: '`null` when the variant has no price of its own and the parent''s applies.'
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  option_id:
                                    type: integer
                                    description: The characteristic.
                                  option_name:
                                    type: string
                                    description: 'Its name, so the variant can be labelled without a second call.'
                                  value_id:
                                    type: integer
                                    description: The value.
                                  value:
                                    type: string
                                    description: Its name.
                              description: 'Which characteristic values this variant stands for, one per characteristic.'
                            images:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                  name:
                                    type: string
                                  path:
                                    type: string
                                    description: Relative to the upload folder.
                                  url:
                                    type: string
                                    description: Absolute.
                                  ordering:
                                    type: integer
                              description: 'The variant''s own images, in the same shape as the product''s.'
                        description: 'Every variant, with its own code, stock, price and images. Empty for a product that does not vary.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: 'The key used in `custom_fields`.'
                            type:
                              type: string
                              description: '`text`, `radio`, `singledropdown`, `file`, and the rest of HikaShop''s field types.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type, before it is mapped to something a client can render.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save the product without it.
                            options:
                              type: array
                              items:
                                type: object
                              description: 'The choices, for a field that has them. Empty for a free text one.'
                            multiple:
                              type: boolean
                              description: Whether more than one choice may be selected.
                            translatable:
                              type: boolean
                              description: 'Whether its value can be translated, which is what the translation routes offer.'
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts, comma separated. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The definitions of the custom fields that apply to this product, so a client can build a form for them.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For custom fields holding a file, the file behind each value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules. nothing: The body held no field this shop knows, so nothing would have been written.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-products-id
      summary: Delete a product
      description: |-
        Deletes a product and its variants.
        
        Orders that already contain it are untouched: an order line records what was sold at the time, and
        it does not stop meaning something because the catalogue changed.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The product that was deleted.
                      deleted:
                        type: boolean
                        description: True when the row is gone.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not delete it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'delete_failed: The shop refused to delete it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/lookup':
    get:
      operationId: get-products-lookup
      summary: Resolve a scanned barcode
      description: |-
        One product from one barcode, for a scanner. It matches the product code and the GTIN, and it
        looks at variants as well as parents, because the thing with a barcode on it is usually the
        variant.
        
        Exactly one product comes back, or `not_found`. It is deliberately not a search: a scanner needs an
        answer, not a list.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: barcode
          in: query
          required: true
          description: What the scanner read. Matched against the product code and the GTIN.
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The product to open. For a variant, this is its parent.'
                      variant_id:
                        type: integer
                        description: 'The variant that was scanned, `0` when the barcode belonged to the parent.'
                      name:
                        type: string
                      code:
                        type: string
                        description: The SKU that matched.
                      gtin:
                        type: string
                        description: 'The barcode held on the record, which may differ in leading zeros from what was scanned.'
                      quantity:
                        type: integer
                        description: 'The stock of whichever record matched, so a stock take needs no second call.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'missing_barcode: No barcode was given.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '404':
          description: 'not_found: Nothing in the shop carries that code.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/low-stock':
    get:
      operationId: get-products-low-stock
      summary: Products running out
      description: |-
        Sellable items at or below a stock threshold, cheapest first to reorder. Variants are listed in
        their own right, since that is where the stock actually sits.
        
        Products that do not track stock are left out: they are not running out of anything.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: threshold
          in: query
          required: false
          description: 'At or below which a product counts as low. Defaults to `5`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Defaults to `20`, capped at `100`.'
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: The parent product.
                        variant_id:
                          type: integer
                          description: 'The variant that is low, `0` when the parent itself is.'
                        name:
                          type: string
                        code:
                          type: string
                          description: The SKU of whichever record is low.
                        quantity:
                          type: integer
                          description: What is left.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/products/{id}/stock':
    post:
      operationId: post-products-id-stock
      summary: 'Set a product''s stock'
      description: |-
        Sets the tracked quantity to an absolute figure, which is what a stock take does. It is not an
        adjustment: send what the shelf holds, not the difference.
        
        A product with variants keeps no stock of its own, so set it on the variant instead. Asking to set
        it on the parent is refused rather than silently ignored.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product or variant to set.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                quantity:
                  type: integer
                  description: 'The new absolute quantity. `-1` turns stock tracking off for this product.'
              required:
                - quantity
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      quantity:
                        type: integer
                        description: 'As stored, so you can confirm what was written.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '409':
          description: 'has_variants: It is a parent: set the stock on one of its variants.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers':
    get:
      operationId: get-customers
      summary: List customers
      description: |-
        The customers the operator may see, with enough to recognise one and to know whether they have
        bought anything.
        
        Guests are included. A shop that lets people order without an account still has a row for each of
        them, and `type` is how you tell the two apart.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: start
          in: query
          required: false
          description: 'Offset. Defaults to `0`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Defaults to `20`, capped at `100`.'
          schema:
            type: integer
        - name: search
          in: query
          required: false
          description: 'Matches the email address, the account name and login, and the first name and surname on any of their addresses, including the two together, so `John Doe` finds a guest who has never had an account.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: 'The HikaShop customer id, which is not the CMS user id.'
                        name:
                          type: string
                          description: 'The account name, or for a guest the name on their default address, since a guest has no account to take one from.'
                        email:
                          type: string
                        type:
                          type: string
                          description: '`registered` for an account, `guest` for someone who ordered without one.'
                        created:
                          type: integer
                          description: Unix timestamp of the first time the shop saw them.
                        order_count:
                          type: integer
                          description: 'How many orders they have placed, so a list can be sorted by worth without a second call.'
                  meta:
                    type: object
                    properties:
                      start:
                        type: integer
                        description: Echoes the offset used.
                      limit:
                        type: integer
                        description: Echoes the page size used.
                      total:
                        type: integer
                        description: 'Customers matching the filter, before paging.'
                  error:
                    type: 'null'
    post:
      operationId: post-customers
      summary: Create a customer
      description: |-
        Creates a customer from an email address and a name, as a guest: no account, no password, nothing
        for them to log in with.
        
        `POST /customers/{id}/account` turns one into a registered customer afterwards.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                email:
                  type: string
                  description: Must not belong to another customer.
                name:
                  type: string
                  description: Their display name.
              required:
                - email
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The customer that was created.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'email_taken: Another customer already has that address.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/discounts':
    get:
      operationId: get-discounts
      summary: List discounts and coupons
      description: |-
        Both kinds of reduction live in one table and are told apart by `type`: a **discount** applies by
        itself when its conditions are met, a **coupon** waits for its code to be entered.
        
        The restriction fields are the interesting part, and they are all lists of ids: which products,
        which categories, which zones, which customers, and the same again for exclusions. An empty list
        means no restriction of that kind rather than none allowed.
      tags:
        - discounts
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: start
          in: query
          required: false
          description: 'Offset. Defaults to `0`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Defaults to `20`, capped at `100`.'
          schema:
            type: integer
        - name: type
          in: query
          required: false
          description: '`discount` or `coupon`, to list one kind.'
          schema:
            type: string
        - name: search
          in: query
          required: false
          description: Matches the code.
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        type:
                          type: string
                          description: '`discount` applies by itself, `coupon` waits for its code.'
                        code:
                          type: string
                          description: What the customer types. Empty on a discount.
                        kind:
                          type: string
                          description: Whether the value is a percentage or a fixed amount.
                        value:
                          type: number
                          description: 'The reduction, read according to `kind`.'
                        currency_id:
                          type: integer
                          description: The currency a fixed amount is in.
                        published:
                          type: boolean
                        start:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp before which it does not apply.
                        end:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp after which it expires.
                        minimum_order:
                          type: number
                          description: 'Order total below which it does not apply, `0` for none.'
                        maximum_order:
                          type: number
                          description: 'Order total above which it stops applying, `0` for none.'
                        quota:
                          type: integer
                          description: 'How many times it may be used in total, `0` for no limit.'
                        quota_per_user:
                          type: integer
                          description: 'How many times one customer may use it, `0` for no limit.'
                        used_times:
                          type: integer
                          description: How many times it already has been.
                        tax_included:
                          type: boolean
                          description: Whether the value is understood as tax included.
                        tax_id:
                          type: integer
                          description: The tax category of the reduction itself.
                        shipping_percent:
                          type: number
                          description: A reduction on the shipping rather than on the goods.
                        minimum_products:
                          type: integer
                          description: Fewest items in the cart for it to apply.
                        maximum_products:
                          type: integer
                          description: Most items for it to still apply.
                        product_ids:
                          type: array
                          items:
                            type: integer
                          description: Restricted to these products. Empty means all of them.
                        exclude_product_ids:
                          type: array
                          items:
                            type: integer
                          description: Never applies to these.
                        category_ids:
                          type: array
                          items:
                            type: integer
                          description: Restricted to these categories.
                        category_childs:
                          type: boolean
                          description: Whether those categories include their sub-categories.
                        exclude_category_ids:
                          type: array
                          items:
                            type: integer
                          description: Never applies in these categories.
                        exclude_category_childs:
                          type: boolean
                          description: Whether those exclusions include sub-categories.
                        zone_ids:
                          type: array
                          items:
                            type: integer
                          description: Restricted to these zones.
                        user_ids:
                          type: array
                          items:
                            type: integer
                          description: Restricted to these customers.
                        access:
                          type: object
                          properties:
                            mode:
                              type: string
                              description: '`all`, `none`, or `groups` when it is restricted to some.'
                            groups:
                              type: array
                              items:
                                type: integer
                              description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                          description: 'Which user groups it is for, in the usual `mode` and `groups` shape.'
                        exclude_access:
                          type: object
                          properties:
                            mode:
                              type: string
                              description: '`all`, `none`, or `groups` when it is restricted to some.'
                            groups:
                              type: array
                              items:
                                type: integer
                              description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                          description: Which user groups it is never for.
                        auto_load:
                          type: boolean
                          description: 'For a coupon, whether the shop applies it without the customer typing it.'
                        product_only:
                          type: boolean
                          description: Whether it reduces only the goods and leaves the fees alone.
                        discounted_products:
                          type: integer
                          description: 'How many products in the cart it applied to, on a discount that has been used.'
                  meta:
                    type: object
                    properties:
                      start:
                        type: integer
                        description: Echoes the offset used.
                      limit:
                        type: integer
                        description: Echoes the page size used.
                      total:
                        type: integer
                        description: 'Rows matching the filter, before paging.'
                  error:
                    type: 'null'
    post:
      operationId: post-discounts
      summary: Create a discount or a coupon
      description: |-
        Creates a reduction and answers with it as the shop stored it.
        
        `type` decides which kind it is: a `coupon` needs a `code`, a `discount` applies by itself. Either
        way `value` is required, read according to `kind`.
      tags:
        - discounts
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: 'Any field of a discount. `type` and `value` are required, and a coupon also needs a `code`. The restriction lists take ids, and an empty list means no restriction of that kind.'
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      type:
                        type: string
                        description: '`discount` applies by itself, `coupon` waits for its code.'
                      code:
                        type: string
                        description: What the customer types. Empty on a discount.
                      kind:
                        type: string
                        description: Whether the value is a percentage or a fixed amount.
                      value:
                        type: number
                        description: 'The reduction, read according to `kind`.'
                      currency_id:
                        type: integer
                        description: The currency a fixed amount is in.
                      published:
                        type: boolean
                      start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which it does not apply.
                      end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it expires.
                      minimum_order:
                        type: number
                        description: 'Order total below which it does not apply, `0` for none.'
                      maximum_order:
                        type: number
                        description: 'Order total above which it stops applying, `0` for none.'
                      quota:
                        type: integer
                        description: 'How many times it may be used in total, `0` for no limit.'
                      quota_per_user:
                        type: integer
                        description: 'How many times one customer may use it, `0` for no limit.'
                      used_times:
                        type: integer
                        description: How many times it already has been.
                      tax_included:
                        type: boolean
                        description: Whether the value is understood as tax included.
                      tax_id:
                        type: integer
                        description: The tax category of the reduction itself.
                      shipping_percent:
                        type: number
                        description: A reduction on the shipping rather than on the goods.
                      minimum_products:
                        type: integer
                        description: Fewest items in the cart for it to apply.
                      maximum_products:
                        type: integer
                        description: Most items for it to still apply.
                      product_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these products. Empty means all of them.
                      exclude_product_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies to these.
                      category_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these categories.
                      category_childs:
                        type: boolean
                        description: Whether those categories include their sub-categories.
                      exclude_category_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies in these categories.
                      exclude_category_childs:
                        type: boolean
                        description: Whether those exclusions include sub-categories.
                      zone_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these zones.
                      user_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these customers.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: 'Which user groups it is for, in the usual `mode` and `groups` shape.'
                      exclude_access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Which user groups it is never for.
                      auto_load:
                        type: boolean
                        description: 'For a coupon, whether the shop applies it without the customer typing it.'
                      product_only:
                        type: boolean
                        description: Whether it reduces only the goods and leaves the fees alone.
                      discounted_products:
                        type: integer
                        description: 'How many products in the cart it applied to, on a discount that has been used.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'code_required: A coupon needs a code. code_taken: Another coupon already uses that code. value_required: A reduction needs a value.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '404':
          description: 'not_found: Not reachable when creating: the same handler serves the update, where it means no such discount.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/categories':
    get:
      operationId: get-categories
      summary: The category tree
      description: |-
        One level of the category tree at a time, unpublished categories included, which is what makes this
        a management view rather than a shop one.
        
        Pass a `parent_id` to walk down. `has_children` tells you whether there is anything below without
        asking, so a tree can be drawn lazily.
        
        HikaShop keeps its manufacturers and its tax categories in the same table as the product
        categories, told apart by their root. Ask for the tree you want.
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: parent_id
          in: query
          required: false
          description: Whose children to list. Omit for the top level.
          schema:
            type: integer
        - name: start
          in: query
          required: false
          description: 'Offset. Defaults to `0`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Defaults to `20`, capped at `100`.'
          schema:
            type: integer
        - name: search
          in: query
          required: false
          description: 'Matches the name, across the whole tree rather than one level.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
                        parent_id:
                          type: integer
                          description: 'Its parent, so a flat answer can be rebuilt into a tree.'
                        published:
                          type: boolean
                        has_children:
                          type: boolean
                          description: Whether anything sits below it.
                        image:
                          type:
                            - string
                            - 'null'
                          description: 'Absolute URL of its image, `null` when it has none.'
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                        description: 'Categories matching, before paging.'
                      start:
                        type: integer
                        description: Echoes the offset used.
                      limit:
                        type: integer
                        description: Echoes the page size used.
                  error:
                    type: 'null'
  '/categories/{id}':
    get:
      operationId: get-categories-id
      summary: Read one category
      description: 'A category with its description, its image, who may see it, and your own category fields.'
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The category id.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own category fields.
                      id:
                        type: integer
                      name:
                        type: string
                      parent_id:
                        type: integer
                        description: Its parent.
                      type:
                        type: string
                        description: 'Which tree it belongs to: `product`, `manufacturer`, `tax`, and so on.'
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      meta_description:
                        type: string
                        description: SEO description.
                      published:
                        type: boolean
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Who may see it.
                      image:
                        type:
                          - string
                          - 'null'
                        description: 'Absolute URL of its image, `null` when it has none.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
    put:
      operationId: put-categories-id
      summary: Update a category
      description: |-
        Changes the fields you send and leaves the rest alone. It serves manufacturers and the other trees
        as well, since they are all categories.
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The category.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: 'Any of `name`, `parent_id`, `published`, `description`, `meta_description`, `access` and `custom_fields`. What you do not send keeps its value.'
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key its value is stored under.
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            type:
                              type: string
                              description: 'What to render: `text`, `radio`, `singledropdown`, `file`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            required:
                              type: boolean
                              description: Whether the shop refuses to save without it.
                            default:
                              type: string
                              description: The value used when none is given.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: Whether its value can be translated.
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: The definitions of your own category fields.
                      name:
                        type: string
                      parent_id:
                        type: integer
                        description: Its parent.
                      type:
                        type: string
                        description: 'Which tree it belongs to: `product`, `manufacturer`, `tax`, and so on.'
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      meta_description:
                        type: string
                        description: SEO description.
                      published:
                        type: boolean
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Who may see it.
                      image:
                        type:
                          - string
                          - 'null'
                        description: 'Absolute URL of its image, `null` when it has none.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such category, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-categories-id
      summary: Delete a category
      description: |-
        Deletes a category. The products in it are not deleted; they simply stop being in it, and a product
        left in no category at all disappears from the shop's listings.
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The category.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The category that was deleted.
                      deleted:
                        type: boolean
                        description: True when the row is gone.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such category, or the operator may not delete it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/massactions':
    get:
      operationId: get-massactions
      summary: 'The shop''s own bulk operations'
      description: |-
        The mass actions the merchant has configured for a listing, so a client can offer the merchant's own
        bulk operations rather than a fixed set of its own.
        
        Whatever they built in the backend appears here, and running one is `POST /massactions/{id}`.
      tags:
        - massactions
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: table
          in: query
          required: true
          description: 'Which listing: `product`, `order`, `user`, `category`, `address`.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: What to run.
                        name:
                          type: string
                          description: As the merchant named it.
                        description:
                          type: string
                          description: 'Their own note about what it does, when they wrote one.'
                        table:
                          type: string
                          description: The listing it belongs to.
                        restricted:
                          type: boolean
                          description: Whether it may only run on a selection rather than on everything matching a filter.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_request: The table is not one the shop has mass actions for.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '403':
          description: 'forbidden: The operator may not view that kind of record.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/media/browse':
    get:
      operationId: get-media-browse
      summary: Browse the upload folder
      description: |-
        Folders and images inside the shop's upload folder, so an existing image can be picked instead of
        uploaded again.
        
        Paths are relative to that folder and never escape it: a path pointing outside is refused rather
        than resolved.
      tags:
        - media
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: folder
          in: query
          required: false
          description: 'Which folder to list, relative to the upload folder. Omit for its root.'
          schema:
            type: string
        - name: offset
          in: query
          required: false
          description: 'Offset into the images, for a folder with many.'
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      folder:
                        type: string
                        description: The folder being listed.
                      parent:
                        type: string
                        description: 'The folder above, for walking back up.'
                      has_parent:
                        type: boolean
                        description: 'False at the root, where there is nothing above.'
                      folders:
                        type: array
                        items:
                          type: object
                        description: The folders inside it.
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                              description: The file name.
                            path:
                              type: string
                              description: 'Relative to the upload folder, which is what a product image stores.'
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                        description: The images inside it.
                      total:
                        type: integer
                        description: How many images the folder holds in all.
                      offset:
                        type: integer
                        description: Echoes the offset used.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such folder, or a path that tried to leave the upload folder.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/zones':
    get:
      operationId: get-zones
      summary: 'Search countries, states and zones'
      description: |-
        The shop's zones, which are its countries, its states and any grouping the merchant made of them.
        Meant for filling a picker: search for a name, or resolve ids you already hold.
        
        An address stores a zone by its namekey rather than its id, which is why both come back.
      tags:
        - zones
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: search
          in: query
          required: false
          description: Matches the name.
          schema:
            type: string
        - name: ids
          in: query
          required: false
          description: 'Comma separated zone ids, to resolve a known set.'
          schema:
            type: string
        - name: type
          in: query
          required: false
          description: 'Restrict to `country`, `state` or a zone group.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        namekey:
                          type: string
                          description: 'What an address stores, such as `FRA` or `US-CA`.'
                        name:
                          type: string
                          description: Translated where the shop has a translation for it.
                        type:
                          type: string
                          description: '`country`, `state`, or the kind of grouping it is.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/users':
    get:
      operationId: get-users
      summary: Search customers for a picker
      description: |-
        A short list of customers matching a search, for the pickers that restrict a price or a discount to
        named people. `GET /customers` is the listing to use for anything else.
      tags:
        - users
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: search
          in: query
          required: false
          description: Matches the name and the email address.
          schema:
            type: string
        - name: ids
          in: query
          required: false
          description: 'Comma separated ids, to resolve a known set.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: 'The customer id, which is what a restriction stores.'
                        name:
                          type: string
                        email:
                          type: string
                          description: Enough to tell two people of the same name apart.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/products/meta':
    get:
      operationId: get-products-meta
      summary: Reference data for the product editor
      description: |-
        Everything a product form needs to offer choices: the currencies with their formatting, the tax
        categories, the characteristics and their values, the units, the field definitions, the warehouses
        and the tags.
        
        One call rather than eight, and it changes rarely, so cache it.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      currencies:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: What a price stores.
                            code:
                              type: string
                              description: 'The ISO code, such as `EUR`.'
                            symbol:
                              type: string
                            name:
                              type: string
                            decimals:
                              type: integer
                              description: How many decimal places to show.
                            decimal_sep:
                              type: string
                              description: The decimal separator.
                            thousands_sep:
                              type: string
                              description: The thousands separator.
                            symbol_before:
                              type: boolean
                              description: Whether the symbol goes before the figure.
                            space:
                              type: boolean
                              description: Whether a space separates the symbol from the figure.
                            rounding_increment:
                              type: number
                              description: 'What amounts are rounded to, for a currency without small coins.'
                        description: 'Every published currency, with enough to format an amount the way the shop does.'
                      main_currency_id:
                        type: integer
                        description: 'The shop''s own currency.'
                      tax_categories:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'What `tax_id` on a product stores.'
                            name:
                              type: string
                            parent_id:
                              type: integer
                              description: 'They live in the category tree, so they have a parent like anything else there.'
                        description: The tax categories a product can be put in.
                      characteristics:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                    description: What a variant refers to.
                                  value:
                                    type: string
                                    description: 'Its name, such as `M`.'
                              description: Its values.
                        description: 'Every characteristic in the shop, with its values, for building variants.'
                      weight_units:
                        type: array
                        items:
                          type: string
                        description: 'The weight units the shop accepts, such as `kg`.'
                      dimension_units:
                        type: array
                        items:
                          type: string
                        description: 'The dimension units the shop accepts, such as `m`.'
                      product_fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key its value is stored under.
                            type:
                              type: string
                              description: 'What to render: `text`, `radio`, `singledropdown`, `file`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save without it.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: Whether its value can be translated.
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: The definitions of your own product fields.
                      category_fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key its value is stored under.
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            type:
                              type: string
                              description: 'What to render: `text`, `radio`, `singledropdown`, `file`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            required:
                              type: boolean
                              description: Whether the shop refuses to save without it.
                            default:
                              type: string
                              description: The value used when none is given.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: Whether its value can be translated.
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: The definitions of your own category fields.
                      bundle_supported:
                        type: boolean
                        description: Whether this edition can sell bundles.
                      warehouses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'What `warehouse_id` on a product stores.'
                            name:
                              type: string
                        description: The warehouses stock can be held in. Empty when the shop has none.
                      tags:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            parent_id:
                              type: integer
                              description: Tags are a tree in both Joomla and WordPress.
                        description: The CMS tags a product can carry.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/products/{id}/prices':
    put:
      operationId: put-products-id-prices
      summary: 'Replace a product''s prices'
      description: |-
        Replaces the whole price set, it does not merge into it. Send every price the product should have,
        including the ones you are not changing; anything you leave out is deleted.
        
        That is deliberate, because a price set is a set: quantity breaks and audience restrictions only
        make sense against each other.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product or variant.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                prices:
                  type: array
                  items:
                    type: object
                  description: 'The complete set. Each needs at least a `value` and a `currency_id`; `min_quantity`, `access`, `users`, `zone_ids`, `start_date` and `end_date` are optional and default to no restriction.'
              required:
                - prices
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        value:
                          type: number
                          description: 'Tax excluded, as stored.'
                        currency_id:
                          type: integer
                        min_quantity:
                          type: integer
                          description: 'From how many items this row applies, which is how a quantity break is expressed.'
                        access:
                          type: object
                          properties:
                            mode:
                              type: string
                              description: '`all`, `none` or `groups`.'
                            groups:
                              type: array
                              items:
                                type: integer
                              description: 'User group ids, not view levels.'
                          description: Which user groups the price is for.
                        users:
                          type: array
                          items:
                            type: integer
                          description: 'Named customers, empty for everyone.'
                        zone_ids:
                          type: array
                          items:
                            type: integer
                          description: 'Zones it applies in, empty for everywhere.'
                        start_date:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp.
                        end_date:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_request: No prices array was sent. invalid_price: A price in the set cannot be stored, and the message says which one and why. Nothing is saved: a set is replaced whole or not at all.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/categories':
    put:
      operationId: put-products-id-categories
      summary: 'Replace a product''s categories'
      description: |-
        Replaces the set of categories the product is in. As with the prices, send the complete set: what
        you leave out is removed.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                categories:
                  type: array
                  items:
                    type: integer
                  description: 'The complete set of category ids. An empty array takes the product out of every category, which hides it from the shop.'
              required:
                - categories
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: The category.
                        name:
                          type: string
                          description: 'Its name, so a client need not look it up.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/translations':
    get:
      operationId: get-products-id-translations
      summary: 'A product''s translations'
      description: |-
        Which of a record's texts can be translated, what they say now in each language, and what the
        original says. `enabled` is false on a single-language shop, where there is nothing to do.
        
        `columns` is the list of translatable things: HikaShop's own (the name, the description, the SEO
        texts) plus every custom field the merchant flagged translatable. It is built the same way the
        backend builds it, so what you are offered is exactly what the shop will store.
        
        The labels and values of a custom field itself are website configuration and are not offered here;
        they belong in the backend.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                        description: 'False on a single-language shop, where the rest is empty.'
                      languages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            code:
                              type: string
                              description: 'The tag, such as `fr-FR`.'
                            shortcode:
                              type: string
                              description: The lower-case underscored form the translation tables key on.
                            site_default:
                              type: boolean
                              description: True for the language the shop is written in.
                        description: 'The shop''s languages. The default one is marked and comes last, because its text is the original rather than a translation of it.'
                      columns:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                              description: 'The column name, such as `product_name`. This is the key to send a translation under.'
                            type:
                              type: string
                              description: '`text` for a line, `textarea` for prose, so a client knows which control to draw.'
                        description: 'What can be translated on this record: HikaShop''s own texts plus every custom field flagged translatable.'
                      values:
                        type: object
                        description: 'What each language says now, keyed by language code and then by column. Keyed by language code, and within that by column name, both of which depend on the shop.'
                        additionalProperties: true
                        x-open: true
                      original:
                        type: object
                        description: 'What the record itself says, keyed by column, so a translator can see what they are translating. Keyed by column name, which depends on the fields this shop has.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such record, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'translation_disabled: This shop does not translate content.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-products-id-translations
      summary: 'Save a product''s translations'
      description: |-
        Writes the translations of one record. Send only the languages you changed; the rest are left alone.
        
        Where the shop uses Falang, the values go into its tables. Where it does not, they become language
        overrides keyed on the original text, which is why renaming a product re-keys its translations
        rather than losing them.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: |-
                An object keyed by language, each holding an object of column name to text:
                
                `{ "2": { "product_name": "Lampe de bureau", "product_description": "…" } }`
                
                The keys are the language ids from `languages` in the GET, and the column names are the ones from
                `columns`. A language you do not send is left alone, and so is a column you do not send within a
                language you do.
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The record that was saved.
                      saved:
                        type: integer
                        description: 'How many languages were written, so a client can tell that something was actually stored.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such record, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'translation_disabled: This shop does not translate content.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/categories/{id}/translations':
    get:
      operationId: get-categories-id-translations
      summary: 'A category''s translations'
      description: |-
        The same as for a product, for a category: its name, its description, its SEO texts and its
        translatable custom fields.
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The category.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                        description: 'False on a single-language shop, where the rest is empty.'
                      languages:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            code:
                              type: string
                              description: 'The tag, such as `fr-FR`.'
                            shortcode:
                              type: string
                              description: The lower-case underscored form the translation tables key on.
                            site_default:
                              type: boolean
                              description: True for the language the shop is written in.
                        description: 'The shop''s languages. The default one is marked and comes last, because its text is the original rather than a translation of it.'
                      columns:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                              description: 'The column name, such as `product_name`. This is the key to send a translation under.'
                            type:
                              type: string
                              description: '`text` for a line, `textarea` for prose, so a client knows which control to draw.'
                        description: 'What can be translated on this record: HikaShop''s own texts plus every custom field flagged translatable.'
                      values:
                        type: object
                        description: 'What each language says now, keyed by language code and then by column. Keyed by language code, and within that by column name, both of which depend on the shop.'
                        additionalProperties: true
                        x-open: true
                      original:
                        type: object
                        description: 'What the record itself says, keyed by column, so a translator can see what they are translating. Keyed by column name, which depends on the fields this shop has.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such record, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'translation_disabled: This shop does not translate content.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-categories-id-translations
      summary: 'Save a category''s translations'
      description: 'The same as for a product, for a category.'
      tags:
        - categories
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The category.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: |-
                An object keyed by language, each holding an object of column name to text:
                
                `{ "2": { "product_name": "Lampe de bureau", "product_description": "…" } }`
                
                The keys are the language ids from `languages` in the GET, and the column names are the ones from
                `columns`. A language you do not send is left alone, and so is a column you do not send within a
                language you do.
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The record that was saved.
                      saved:
                        type: integer
                        description: How many languages were written.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such record, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'translation_disabled: This shop does not translate content.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/stats/dashboard':
    get:
      operationId: get-stats-dashboard
      summary: Dashboard figures
      description: |-
        Revenue, orders, average basket and new customers over a period, the same figures again for the
        period before it so a change can be shown, the series behind them, and the best sellers.
        
        Only orders the shop counts as sold are included, so a cancelled order does not inflate a total.
        Amounts are in the shop's own currency.
      tags:
        - stats
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: range
          in: query
          required: false
          description: '`day`, `week`, `month` or `year`. Defaults to `month`.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      range:
                        type: string
                        description: The period the figures cover.
                      currency_id:
                        type: integer
                        description: 'The shop''s currency, which every amount here is in.'
                      totals:
                        type: object
                        properties:
                          revenue:
                            type: number
                            description: Taken in the period.
                          orders:
                            type: integer
                            description: How many were placed.
                          average_order:
                            type: number
                            description: Revenue divided by orders.
                          customers:
                            type: integer
                            description: New customers in the period.
                        description: The four headline figures.
                      previous:
                        type: object
                        properties:
                          revenue:
                            type: number
                            description: Taken in the preceding period.
                          orders:
                            type: integer
                            description: Placed in it.
                          average_order:
                            type: number
                            description: Its average basket.
                          customers:
                            type: integer
                            description: New customers in it.
                        description: 'The same four figures for the preceding period of the same length, for a comparison.'
                      series_granularity:
                        type: string
                        description: 'Whether the series is by day, week, month or year, which follows from the range.'
                      revenue_series:
                        type: array
                        items:
                          type: object
                          properties:
                            date:
                              type: string
                              description: 'The interval, as a date.'
                            revenue:
                              type: number
                              description: Taken in it.
                        description: 'One point per interval, for a chart.'
                      top_products:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            quantity:
                              type: integer
                              description: How many were sold.
                        description: The best sellers of the period.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/groups':
    get:
      operationId: get-groups
      summary: The user groups
      description: |-
        The CMS user groups, for the pickers that restrict a price, a discount or a product to some of
        them. The list is the same shape on Joomla and on WordPress, which is the point of it: a client does
        not need to know which it is talking to.
        
        These are the ids an `access` object holds. They are user groups and not Joomla view levels.
      tags:
        - groups
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: What an access object stores.
                        title:
                          type: string
                          description: The name of the group.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/orders/{id}/products/precompute':
    get:
      operationId: get-orders-id-products-precompute
      summary: Price a product in an order
      description: |-
        What a product would cost on this order, before adding it. The order's customer, currency and zone
        all bear on the price, so this asks the shop rather than guessing from the catalogue.
        
        Use it to show a line before it is committed, then `POST /orders/{id}/products` to add it.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
        - name: product_id
          in: query
          required: true
          description: The product or variant to price.
          schema:
            type: integer
        - name: quantity
          in: query
          required: false
          description: 'How many, which matters where the product has quantity breaks. Defaults to `1`.'
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      product_id:
                        type: integer
                        description: What was priced.
                      name:
                        type: string
                      code:
                        type: string
                        description: Its SKU.
                      quantity:
                        type: integer
                        description: The quantity the price was worked out for.
                      price:
                        type: number
                        description: 'Unit price, tax excluded, in the order currency.'
                      tax:
                        type: number
                        description: 'Tax per unit, worked out for this order rather than in general.'
                      tax_namekeys:
                        type: array
                        items:
                          type: string
                        description: Which tax rates that came from.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order or no such product, or the operator may not see them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers/{id}':
    get:
      operationId: get-customers-id
      summary: Read one customer
      description: |-
        A customer with their addresses, their orders, which groups they are in and your own customer
        fields.
        
        `can_edit_account` and `groups_editable` are worth reading before drawing a form: they say whether
        this operator may change this particular account at all, so a client can leave the controls out
        rather than offer something that will be refused. A customer can have many addresses and one
        default of each kind.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The customer id.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer, or the operator may not see them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-customers-id
      summary: Delete a customer
      description: |-
        Removes a customer and their addresses.
        
        A customer with orders is refused, which is what the backend does too: delete their orders first,
        and then the customer.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer id.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                        description: True when the row is gone.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer, or the operator may not delete them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'has_orders: They have orders. Delete those first. delete_failed: The shop refused to delete the row.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-customers-id
      summary: 'Update a customer''s profile'
      description: |-
        Changes the name, the email address, the login, the password, the user groups and your own customer
        fields, and answers with the customer as it now stands.
        
        The password is written straight into the CMS account and is never returned by anything. Changing
        the email of a registered customer changes the account they log in with, which is why a clash is
        refused rather than silently ignored.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer id.
          schema:
            type: integer
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Their display name.
                email:
                  type: string
                  description: Must not belong to another account.
                username:
                  type: string
                  description: 'The login, for a registered customer.'
                password:
                  type: string
                  description: A new password. Send it only when changing it.
                groups:
                  type: array
                  items:
                    type: integer
                  description: 'The user groups they belong to, as ids from `GET /groups`.'
                custom_fields:
                  type: object
                  description: 'Your own customer fields, keyed by namekey.'
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_email: The email address is not one. forbidden_target: The operator may not change this particular account, which is how a super user is protected from being edited by staff. username_taken: Another account has that login. email_taken: Another account has that email address. account_save_failed: The CMS refused to save the account. invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers/{id}/account':
    post:
      operationId: post-customers-id-account
      summary: Give a guest an account
      description: |-
        Turns a guest into a registered customer, keeping their orders and addresses, and answers with the
        customer as it now stands.
        
        This is the useful direction: someone ordered without an account, then asked for one. Creating the
        account separately would leave their history behind.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The guest customer.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                username:
                  type: string
                  description: The login to create.
                password:
                  type: string
                  description: Their password. Never returned by anything afterwards.
                name:
                  type: string
                  description: 'Their display name, defaulting to the one on the guest record.'
                groups:
                  type: array
                  items:
                    type: integer
                  description: The user groups to put them in.
              required:
                - username
                - password
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'already_registered: They already have an account. missing_credentials: A username and a password are both required. invalid_email: The address on the guest record is not usable as an account email. email_taken: Another account has that email address. username_taken: Another account has that login. account_save_failed: The CMS refused to create the account.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products':
    post:
      operationId: post-products
      summary: Create a product
      description: |-
        Creates a product and answers with its id. Only the name is required; everything else can be set
        now or later with `PUT /products/{id}`.
        
        Prices, images and categories are separate calls, so a new product is not sellable until it has at
        least one price.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes: []
      security: []
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: |-
                Any of the fields a product carries: `name` (required), `code`, `description`, `published`, `msrp`,
                `gtin`, `condition`, `weight`, `weight_unit`, `width`, `height`, `length`, `dimension_unit`,
                `min_per_order`, `max_per_order`, `sale_start`, `sale_end`, `page_title`, `meta_description`,
                `keywords`, `canonical`, `url`, `alias`, `tax_id`, `manufacturer_id`, `contact`, `warehouse_id`,
                plus `access` and `custom_fields`.
                
                They mean what they mean on `GET /products/{id}`. A field the shop's own schema does not have is
                ignored rather than refused, which is what lets one client talk to shops of different vintages.
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      code:
                        type: string
                        description: The SKU. Unique within the shop.
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      description_type:
                        type: string
                        description: Which editor the description was written with.
                      published:
                        type: boolean
                      quantity:
                        type: integer
                        description: '`-1` when this product does not track stock, which is not the same as `0`.'
                      msrp:
                        type: integer
                        description: 'The manufacturer''s suggested price, shown struck through when the shop is configured to.'
                      gtin:
                        type: string
                        description: 'The barcode: EAN, UPC or ISBN. This is what `GET /products/lookup` matches on.'
                      condition:
                        type: string
                        description: 'New, used, refurbished. Used by the feeds rather than by the shop itself.'
                      weight:
                        type: integer
                        description: 'Shipping weight, in `weight_unit`.'
                      weight_unit:
                        type: string
                        description: '`kg`, `g`, `lb` or `oz`.'
                      width:
                        type: integer
                        description: 'In `dimension_unit`.'
                      height:
                        type: integer
                        description: 'In `dimension_unit`.'
                      length:
                        type: integer
                        description: 'In `dimension_unit`.'
                      dimension_unit:
                        type: string
                        description: '`m`, `cm`, `mm`, `ft` or `in`.'
                      min_per_order:
                        type: integer
                        description: 'The smallest quantity a customer may order, `0` for no minimum.'
                      max_per_order:
                        type: integer
                        description: 'The largest, `0` for no maximum.'
                      sale_start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which the product is not on sale.
                      sale_end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it is no longer sold.
                      page_title:
                        type: string
                        description: 'SEO title, empty to use the name.'
                      meta_description:
                        type: string
                        description: SEO description.
                      keywords:
                        type: string
                        description: SEO keywords.
                      canonical:
                        type: string
                        description: 'A canonical URL, when this page should point at another.'
                      url:
                        type: string
                        description: The address of the product page on the shop.
                      alias:
                        type: string
                        description: The slug used in that address.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User group ids, meaningful only when the mode is `groups`.'
                        description: 'Who may see the product: `mode` (`all`, `none` or `groups`) and `groups`, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                      contact:
                        type: boolean
                        description: Whether this product is enquired about rather than bought.
                      warehouse_id:
                        type: integer
                        description: 'The warehouse holding the stock, `0` when the shop has none.'
                      type:
                        type: string
                        description: '`main` for a product, `variant` for one of its variants.'
                      parent_id:
                        type: integer
                        description: 'The parent product when this is a variant, `0` otherwise.'
                      value_ids:
                        type: array
                        items:
                          type: integer
                        description: 'For a variant, the characteristic values it stands for.'
                      manufacturer_id:
                        type: integer
                        description: 'The brand, `0` when unset.'
                      manufacturer_name:
                        type: string
                        description: 'Its name, saving a second call.'
                      tax_id:
                        type: integer
                        description: 'The tax category, `0` when the product is untaxed.'
                      tax_name:
                        type: string
                        description: Its name.
                      tax_rate:
                        type: number
                        description: 'The rate as a fraction, so `0.2` is twenty percent.'
                      prices:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            value:
                              type: string
                              description: 'Tax excluded, as stored.'
                            currency_id:
                              type: string
                            min_quantity:
                              type: string
                              description: 'From how many items this row applies, which is how quantity breaks are expressed.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: 'Who this price is for, in the same shape as the product access.'
                            users:
                              type: array
                              items:
                                type: integer
                              description: 'Named customers, empty for everyone.'
                            zone_ids:
                              type: array
                              items:
                                type: integer
                              description: 'Zones this price applies in, empty for everywhere.'
                            start_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                            end_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                        description: 'Every price row, including the restricted ones. A product with none is not sellable.'
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: string
                            description:
                              type: string
                              description: The alt text.
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: Who may see it.
                            free_download:
                              type: string
                              description: 'Files only; meaningless on an image.'
                        description: 'In the order the editor shows them; the first is the main image.'
                      files:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: Absolute.
                            ordering:
                              type: string
                            description:
                              type: string
                            access:
                              type: object
                              description: Who may download it.
                            free_download:
                              type: string
                              description: Whether it can be downloaded without buying the product.
                        description: 'Downloadable files, in the same shape as the images.'
                      categories:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                        description: The categories the product is in.
                      bundle:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                              description: How many of it the bundle contains.
                        description: 'The products this one is made of, when it is a bundle.'
                      options:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products offered as options alongside this one.
                      related:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products shown as related.
                      tags:
                        type: array
                        items:
                          type: integer
                        description: CMS tag ids.
                      characteristics:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The characteristic, such as Size.'
                            name:
                              type: string
                              description: Its name.
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                    description: 'The value id, which is what a variant refers to.'
                                  value:
                                    type: string
                                    description: 'Its name, such as `M`.'
                              description: 'The values of it this product uses, such as S, M and L.'
                        description: The characteristics this product varies on. Empty when it has no variants.
                      variants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The variant is a product in its own right, and this is its id.'
                            code:
                              type: string
                              description: Its own SKU.
                            quantity:
                              type: integer
                              description: 'Its own stock. This is the figure to change, not the parent''s.'
                            published:
                              type: boolean
                            price:
                              type:
                                - number
                                - 'null'
                              description: '`null` when the variant has no price of its own and the parent''s applies.'
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  option_id:
                                    type: integer
                                    description: The characteristic.
                                  option_name:
                                    type: string
                                    description: 'Its name, so the variant can be labelled without a second call.'
                                  value_id:
                                    type: integer
                                    description: The value.
                                  value:
                                    type: string
                                    description: Its name.
                              description: 'Which characteristic values this variant stands for, one per characteristic.'
                            images:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                  name:
                                    type: string
                                  path:
                                    type: string
                                    description: Relative to the upload folder.
                                  url:
                                    type: string
                                    description: Absolute.
                                  ordering:
                                    type: integer
                              description: 'The variant''s own images, in the same shape as the product''s.'
                        description: 'Every variant, with its own code, stock, price and images. Empty for a product that does not vary.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: 'The key used in `custom_fields`.'
                            type:
                              type: string
                              description: '`text`, `radio`, `singledropdown`, `file`, and the rest of HikaShop''s field types.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type, before it is mapped to something a client can render.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save the product without it.
                            options:
                              type: array
                              items:
                                type: object
                              description: 'The choices, for a field that has them. Empty for a free text one.'
                            multiple:
                              type: boolean
                              description: Whether more than one choice may be selected.
                            translatable:
                              type: boolean
                              description: 'Whether its value can be translated, which is what the translation routes offer.'
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts, comma separated. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The definitions of the custom fields that apply to this product, so a client can build a form for them.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For custom fields holding a file, the file behind each value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save the product.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    get:
      operationId: get-products
      summary: List products
      description: |-
        The catalogue as the backend sees it, unpublished products included, which is what makes this
        different from the front end.
        
        Variants are not listed on their own. A parent carries `has_variants`, and
        `GET /products/{id}` returns the variants themselves.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: start
          in: query
          required: false
          description: 'Offset into the result set. Defaults to `0`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Page size. Defaults to `20` and is capped at `100`.'
          schema:
            type: integer
        - name: search
          in: query
          required: false
          description: Matches the product name and the product code.
          schema:
            type: string
        - name: ids
          in: query
          required: false
          description: 'Comma separated product ids, to resolve a known set in one call.'
          schema:
            type: string
        - name: category_id
          in: query
          required: false
          description: Restrict to one category.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        name:
                          type: string
                        code:
                          type: string
                          description: The SKU. Unique within the shop.
                        quantity:
                          type: integer
                          description: '`-1` when the product does not track stock, which is not the same as `0`.'
                        published:
                          type: boolean
                        has_variants:
                          type: boolean
                          description: 'Ask `GET /products/{id}` for the variants themselves.'
                        image:
                          type:
                            - string
                            - 'null'
                          description: 'Absolute URL of the main image, or `null`.'
                        price:
                          type:
                            - number
                            - 'null'
                          description: '`null` when the product has no price row at all.'
                        currency_id:
                          type: integer
                          description: 'A row in the shop''s currency table, not an ISO code.'
                        custom_fields:
                          type: object
                          description: 'The listing values of this product, keyed by field namekey. The keys are whatever this shop has configured, so there is no list to give. `fields` in the envelope says what they are.'
                          additionalProperties: true
                          x-open: true
                  meta:
                    type: object
                    properties:
                      start:
                        type: integer
                        description: Echoes the offset used.
                      limit:
                        type: integer
                        description: Echoes the page size used.
                      total:
                        type: integer
                        description: 'Rows matching the filter, before paging. This is how you know there is another page.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: 'The custom fields shown on listings, so a client can label the values it just received.'
                  error:
                    type: 'null'
  '/products/{id}/variants':
    put:
      operationId: put-products-id-variants
      summary: Reconcile the variant set
      description: |-
        Takes the variants a product should have and makes the shop agree: it creates the ones that are
        missing, updates the ones that exist and removes the ones you left out.
        
        A variant is identified by the characteristic values it stands for rather than by an id, because
        that is what makes it that variant. Send the whole set.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The parent product.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                variants:
                  type: array
                  items:
                    type: object
                  description: 'The complete set. Each needs the characteristic `values` it stands for, and may carry `code`, `quantity`, `published` and `price`.'
              required:
                - variants
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      characteristics:
                        type: array
                        items:
                          type: object
                        description: 'The characteristics the product now varies on, as they stand after the change.'
                      variants:
                        type: array
                        items:
                          type: object
                        description: 'The variants as they now stand, in the same shape as on the product.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/variants/{vid}':
    put:
      operationId: put-products-id-variants-vid
      summary: Edit one variant
      description: |-
        Changes one variant without touching the others, which is what a stock correction or a price change
        on a single size needs.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The parent product.
          schema:
            type: integer
        - name: vid
          in: path
          required: true
          description: The variant.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: 'Any of `code`, `quantity`, `published` and `price`, and your own product fields. What you do not send is left alone.'
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      code:
                        type: string
                        description: The SKU. Unique within the shop.
                      description:
                        type: string
                        description: 'The long description, as HTML.'
                      description_type:
                        type: string
                        description: Which editor the description was written with.
                      published:
                        type: boolean
                      quantity:
                        type: integer
                        description: '`-1` when this product does not track stock, which is not the same as `0`.'
                      msrp:
                        type: integer
                        description: 'The manufacturer''s suggested price, shown struck through when the shop is configured to.'
                      gtin:
                        type: string
                        description: 'The barcode: EAN, UPC or ISBN. This is what `GET /products/lookup` matches on.'
                      condition:
                        type: string
                        description: 'New, used, refurbished. Used by the feeds rather than by the shop itself.'
                      weight:
                        type: integer
                        description: 'Shipping weight, in `weight_unit`.'
                      weight_unit:
                        type: string
                        description: '`kg`, `g`, `lb` or `oz`.'
                      width:
                        type: integer
                        description: 'In `dimension_unit`.'
                      height:
                        type: integer
                        description: 'In `dimension_unit`.'
                      length:
                        type: integer
                        description: 'In `dimension_unit`.'
                      dimension_unit:
                        type: string
                        description: '`m`, `cm`, `mm`, `ft` or `in`.'
                      min_per_order:
                        type: integer
                        description: 'The smallest quantity a customer may order, `0` for no minimum.'
                      max_per_order:
                        type: integer
                        description: 'The largest, `0` for no maximum.'
                      sale_start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which the product is not on sale.
                      sale_end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it is no longer sold.
                      page_title:
                        type: string
                        description: 'SEO title, empty to use the name.'
                      meta_description:
                        type: string
                        description: SEO description.
                      keywords:
                        type: string
                        description: SEO keywords.
                      canonical:
                        type: string
                        description: 'A canonical URL, when this page should point at another.'
                      url:
                        type: string
                        description: The address of the product page on the shop.
                      alias:
                        type: string
                        description: The slug used in that address.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User group ids, meaningful only when the mode is `groups`.'
                        description: 'Who may see the product: `mode` (`all`, `none` or `groups`) and `groups`, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                      contact:
                        type: boolean
                        description: Whether this product is enquired about rather than bought.
                      warehouse_id:
                        type: integer
                        description: 'The warehouse holding the stock, `0` when the shop has none.'
                      type:
                        type: string
                        description: '`main` for a product, `variant` for one of its variants.'
                      parent_id:
                        type: integer
                        description: 'The parent product when this is a variant, `0` otherwise.'
                      value_ids:
                        type: array
                        items:
                          type: integer
                        description: 'For a variant, the characteristic values it stands for.'
                      manufacturer_id:
                        type: integer
                        description: 'The brand, `0` when unset.'
                      manufacturer_name:
                        type: string
                        description: 'Its name, saving a second call.'
                      tax_id:
                        type: integer
                        description: 'The tax category, `0` when the product is untaxed.'
                      tax_name:
                        type: string
                        description: Its name.
                      tax_rate:
                        type: number
                        description: 'The rate as a fraction, so `0.2` is twenty percent.'
                      prices:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            value:
                              type: string
                              description: 'Tax excluded, as stored.'
                            currency_id:
                              type: string
                            min_quantity:
                              type: string
                              description: 'From how many items this row applies, which is how quantity breaks are expressed.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: 'Who this price is for, in the same shape as the product access.'
                            users:
                              type: array
                              items:
                                type: integer
                              description: 'Named customers, empty for everyone.'
                            zone_ids:
                              type: array
                              items:
                                type: integer
                              description: 'Zones this price applies in, empty for everywhere.'
                            start_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                            end_date:
                              type:
                                - integer
                                - 'null'
                              description: Unix timestamp.
                        description: 'Every price row, including the restricted ones. A product with none is not sellable.'
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: integer
                            description:
                              type: string
                              description: The alt text.
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: As above.
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: As above.
                              description: Who may see it.
                            free_download:
                              type: boolean
                              description: 'Files only; meaningless on an image.'
                        description: 'In the order the editor shows them; the first is the main image.'
                      files:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: Absolute.
                            ordering:
                              type: string
                            description:
                              type: string
                            access:
                              type: object
                              description: Who may download it.
                            free_download:
                              type: string
                              description: Whether it can be downloaded without buying the product.
                        description: 'Downloadable files, in the same shape as the images.'
                      categories:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                        description: The categories the product is in.
                      bundle:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                              description: How many of it the bundle contains.
                        description: 'The products this one is made of, when it is a bundle.'
                      options:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products offered as options alongside this one.
                      related:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            code:
                              type: string
                            quantity:
                              type: string
                        description: Products shown as related.
                      tags:
                        type: array
                        items:
                          type: integer
                        description: CMS tag ids.
                      characteristics:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The characteristic, such as Size.'
                            name:
                              type: string
                              description: Its name.
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                    description: 'The value id, which is what a variant refers to.'
                                  value:
                                    type: string
                                    description: 'Its name, such as `M`.'
                              description: 'The values of it this product uses, such as S, M and L.'
                        description: The characteristics this product varies on. Empty when it has no variants.
                      variants:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The variant is a product in its own right, and this is its id.'
                            code:
                              type: string
                              description: Its own SKU.
                            quantity:
                              type: integer
                              description: 'Its own stock. This is the figure to change, not the parent''s.'
                            published:
                              type: boolean
                            price:
                              type:
                                - number
                                - 'null'
                              description: '`null` when the variant has no price of its own and the parent''s applies.'
                            values:
                              type: array
                              items:
                                type: object
                                properties:
                                  option_id:
                                    type: integer
                                    description: The characteristic.
                                  option_name:
                                    type: string
                                    description: 'Its name, so the variant can be labelled without a second call.'
                                  value_id:
                                    type: integer
                                    description: The value.
                                  value:
                                    type: string
                                    description: Its name.
                              description: 'Which characteristic values this variant stands for, one per characteristic.'
                            images:
                              type: array
                              items:
                                type: object
                                properties:
                                  id:
                                    type: integer
                                  name:
                                    type: string
                                  path:
                                    type: string
                                    description: Relative to the upload folder.
                                  url:
                                    type: string
                                    description: Absolute.
                                  ordering:
                                    type: integer
                              description: 'The variant''s own images, in the same shape as the product''s.'
                        description: 'Every variant, with its own code, stock, price and images. Empty for a product that does not vary.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: 'The key used in `custom_fields`.'
                            type:
                              type: string
                              description: '`text`, `radio`, `singledropdown`, `file`, and the rest of HikaShop''s field types.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type, before it is mapped to something a client can render.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save the product without it.
                            options:
                              type: array
                              items:
                                type: object
                              description: 'The choices, for a field that has them. Empty for a free text one.'
                            multiple:
                              type: boolean
                              description: Whether more than one choice may be selected.
                            translatable:
                              type: boolean
                              description: 'Whether its value can be translated, which is what the translation routes offer.'
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts, comma separated. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The definitions of the custom fields that apply to this product, so a client can build a form for them.'
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For custom fields holding a file, the file behind each value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such variant, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}/coupon':
    post:
      operationId: post-orders-id-coupon
      summary: Apply a coupon to an order
      description: |-
        Applies a coupon by its code and re-totals the order.
        
        The shop validates it again here against this order: its dates, its quota, its minimum, the
        products in the cart and the customer. A coupon that appears in `GET /coupons` can still be refused
        for this particular order, which is the point of validating rather than trusting the list.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: 'The coupon code, as the customer would type it.'
              required:
                - code
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order.
                      fees:
                        type: object
                        properties:
                          discount:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: 'A positive figure, already subtracted from the total.'
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              code:
                                type: string
                                description: 'The coupon code, empty for a discount applied by hand.'
                            description: 'Its `amount`, its `tax`, the `tax_namekeys` behind that tax, and the coupon `code` when one was used.'
                          shipping:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that handled it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The shipping charge and what carried it.
                          payment:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that took it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The payment fee and what took it.
                        description: 'The discount, shipping and payment amounts of the order.'
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: 'The order totalled, so a client need not compute it and disagree with the shop.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_coupon: No such code, or it does not apply to this order.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-orders-id-coupon
      summary: Remove the discount from an order
      description: |-
        Takes the coupon or the hand-applied discount off the order and re-totals it. There is nothing to
        send.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order.
                      fees:
                        type: object
                        properties:
                          discount:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: 'A positive figure, already subtracted from the total.'
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              code:
                                type: string
                                description: 'The coupon code, empty for a discount applied by hand.'
                            description: 'Its `amount`, its `tax`, the `tax_namekeys` behind that tax, and the coupon `code` when one was used.'
                          shipping:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that handled it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The shipping charge and what carried it.
                          payment:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that took it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The payment fee and what took it.
                        description: 'The discount, shipping and payment amounts of the order.'
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: 'The order totalled, so a client need not compute it and disagree with the shop.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}/products':
    post:
      operationId: post-orders-id-products
      summary: Add a line to an order
      description: |-
        Adds a product to an existing order and re-totals it.
        
        Send a `price` only to override what the shop would charge; leave it out and the order's own
        pricing applies, which is what `GET /orders/{id}/products/precompute` shows you beforehand.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                product_id:
                  type: integer
                  description: The product or variant to add.
                quantity:
                  type: integer
                  description: 'How many. Defaults to `1`.'
                price:
                  type: number
                  description: 'Unit price, tax excluded, to override the shop. Omit to let the shop price it.'
                tax_namekeys:
                  type: array
                  items:
                    type: string
                  description: 'Which tax rates to apply, when overriding the price. Omit to let the shop decide.'
              required:
                - product_id
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order.
                      items:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The line id, which is what the line routes take. It is not the product id.'
                            name:
                              type: string
                              description: The product as it was named when ordered.
                            code:
                              type: string
                              description: Its SKU at the time.
                            quantity:
                              type: integer
                            price:
                              type: number
                              description: 'Unit price, tax excluded, as agreed at the time.'
                            tax:
                              type: number
                              description: Tax on the line.
                            editable:
                              type: boolean
                              description: False once the line can no longer be changed.
                        description: 'The lines as they now stand, in the same shape as on the order.'
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: 'The order totalled, so a client need not compute it and disagree with the shop.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order or product, or the operator may not change the order.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}/products/{lineId}':
    put:
      operationId: put-orders-id-products-lineid
      summary: 'Change a line''s quantity'
      description: |-
        Changes how many of one line the order holds, and re-totals it. A quantity of `0` removes the line.
        
        The id in the path is the **line** id from `items`, not the product id: the same product can appear
        on an order more than once.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
        - name: lineId
          in: path
          required: true
          description: 'The line, from `items[].id` on the order.'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                quantity:
                  type: integer
                  description: 'The new quantity. `0` removes the line.'
              required:
                - quantity
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order.
                      items:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The line id, which is what the line routes take. It is not the product id.'
                            name:
                              type: string
                              description: The product as it was named when ordered.
                            code:
                              type: string
                              description: Its SKU at the time.
                            quantity:
                              type: integer
                            price:
                              type: number
                              description: 'Unit price, tax excluded, as agreed at the time.'
                            tax:
                              type: number
                              description: Tax on the line.
                            editable:
                              type: boolean
                              description: False once the line can no longer be changed.
                        description: The lines as they now stand.
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: 'The order totalled, so a client need not compute it and disagree with the shop.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order or line, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/massactions/{id}':
    post:
      operationId: post-massactions-id
      summary: Run a mass action
      description: |-
        Runs one of the merchant's own bulk operations over a selection.
        
        It runs the same code the backend runs, so whatever the action does there it does here, including
        whatever a third-party plugin added to it. Give it the ids to work on.
      tags:
        - massactions
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: 'The mass action, from `GET /massactions`.'
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                ids:
                  type: array
                  items:
                    type: integer
                  description: 'The records to run it over, from the listing the action belongs to.'
              required:
                - ids
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      ok:
                        type: boolean
                        description: Whether the action reported success.
                      count:
                        type: integer
                        description: How many records it worked on.
                      report:
                        type: string
                        description: 'Whatever the action had to say, ready to show. Its wording is the action''s own.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_request: No ids were sent, or the action is not one this shop has.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '403':
          description: 'forbidden: The operator may not work on that kind of record.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders':
    post:
      operationId: post-orders
      summary: Create an order by hand
      description: |-
        Creates an empty order for a customer, to be filled in with lines, fees and an address.
        
        Give it either an existing `user_id` or a `guest` with at least an email address, which is how an
        order gets taken over the telephone from somebody who has never bought before.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                user_id:
                  type: integer
                  description: 'An existing customer. Give this or `guest`.'
                guest:
                  type: object
                  description: 'A new guest customer, needing at least `email` and usually a name.'
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The order that was created, to add lines to.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'no_customer: Neither a user_id nor a usable guest was given.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be created.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    get:
      operationId: get-orders
      summary: List orders
      description: |-
        The orders the operator may see, newest first. It is the listing the app's order screen is built
        on, so it carries just enough to draw a row and no more; ask for one order when you need the rest.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: start
          in: query
          required: false
          description: 'Offset. Defaults to `0`.'
          schema:
            type: integer
        - name: limit
          in: query
          required: false
          description: 'Defaults to `20`, capped at `100`.'
          schema:
            type: integer
        - name: search
          in: query
          required: false
          description: Matches the order number and the customer.
          schema:
            type: string
        - name: status
          in: query
          required: false
          description: 'A status namekey, as listed by `GET /statuses`.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                          description: 'The order id, which is what every other order route takes.'
                        number:
                          type: string
                          description: 'The order number the customer sees, which is not the id.'
                        status:
                          type: string
                          description: 'A namekey, not a label. `GET /statuses` translates it.'
                        created:
                          type: integer
                          description: Unix timestamp.
                        total:
                          type: number
                          description: 'What the customer owes, tax included, in the order currency.'
                        currency_id:
                          type: integer
                          description: 'The order keeps the currency it was placed in, which need not be the shop default.'
                        customer:
                          type: object
                          properties:
                            name:
                              type: string
                            email:
                              type: string
                          description: Enough to name the buyer in a list.
                        custom_fields:
                          type: object
                          description: 'The listing values of your own order fields, keyed by namekey. The keys are whatever this shop has configured; `fields` in the envelope says what they are.'
                          additionalProperties: true
                          x-open: true
                  meta:
                    type: object
                    properties:
                      start:
                        type: integer
                        description: Echoes the offset used.
                      limit:
                        type: integer
                        description: Echoes the page size used.
                      total:
                        type: integer
                        description: 'Orders matching the filter, before paging.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: 'The definitions behind `custom_fields`.'
                  error:
                    type: 'null'
  '/orders/{id}/fields':
    put:
      operationId: put-orders-id-fields
      summary: 'Save an order''s custom fields'
      description: 'Writes the merchant''s own order fields. Only the fields you send are touched.'
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: Keyed by field namekey.
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order.
                      custom_fields:
                        type: object
                        description: The values as they now stand. The keys depend on the shop.
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}/address/{type}':
    put:
      operationId: put-orders-id-address-type
      summary: Save an address of an order
      description: |-
        Writes the billing or shipping address of an order, using the fields `GET` gave you.
        
        The address on an order is its own copy, so changing it here does not change the customer's address
        book, and vice versa. That is deliberate: an invoice should not change because somebody moved.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
        - name: type
          in: path
          required: true
          description: '`billing` or `shipping`.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: 'Keyed by address field namekey, the same keys `GET` returned in `values`.'
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      type:
                        type: string
                        description: Which address was written.
                      address_id:
                        type: integer
                        description: 'The address row, created if there was none.'
                      values:
                        type: object
                        description: The values as stored. Keyed by whatever address fields this shop has.
                        additionalProperties: true
                        x-open: true
                      country_name:
                        type: string
                        description: The country spelled out.
                      state_name:
                        type: string
                        description: 'The state spelled out, empty where the country has none.'
                      summary:
                        type: object
                        properties:
                          name:
                            type: string
                          company:
                            type: string
                          formatted:
                            type: object
                            properties:
                              text:
                                type: string
                                description: 'Several lines, for an invoice or a label.'
                              one_line:
                                type: string
                                description: 'One line, for a list.'
                            description: 'Laid out the way this shop lays addresses out, which follows its address format setting.'
                          street:
                            type: string
                          city:
                            type: string
                          post_code:
                            type: string
                        description: 'The address ready to show, without re-reading the order.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules. invalid_address: The address is not one the shop will accept, usually a missing required field.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    get:
      operationId: get-orders-id-address-type
      summary: 'An address of an order, with its form'
      description: |-
        The billing or shipping address of an order, together with the form the shop would use to edit it.
        
        The form is sent with the values because a shop decides its own address fields: which exist, which
        are required, and in what order. Build the form from `fields` rather than assuming a shape, and
        `PUT` the same keys back.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The order id.
          schema:
            type: integer
        - name: type
          in: path
          required: true
          description: '`billing` or `shipping`.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      type:
                        type: string
                        description: Which address this is.
                      address_id:
                        type: integer
                        description: 'The address row, `0` when the order has none of that kind.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key to send the value back under.
                            type:
                              type: string
                              description: 'What to render: `text`, `zone`, `singledropdown`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save the address without it.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them. A country or a state is filled from the zones rather than from here.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: 'Not meaningful on an address, where values are the customer''s own words.'
                            upload_dir:
                              type: string
                              description: Unused on an address field.
                            allowed_extensions:
                              type: string
                              description: Unused on an address field.
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The shop''s address fields, in display order.'
                      values:
                        type: object
                        description: 'The current values, keyed by field namekey. Keyed by whatever address fields this shop has.'
                        additionalProperties: true
                        x-open: true
                      country_name:
                        type: string
                        description: 'The country spelled out, since the value itself is a zone id.'
                      state_name:
                        type: string
                        description: 'The state spelled out, empty where the country has none.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/images':
    post:
      operationId: post-products-id-images
      summary: Attach an image or a file
      description: |-
        Adds an image or a downloadable file to a product, either by sending the bytes or by pointing at
        something already in the upload folder.
        
        Send `data` as base64 to upload, or `path` to attach a file that `GET /media/browse` already showed
        you. The extension is checked against what the shop allows, so a refusal is the shop's policy rather
        than a fault.
        
        The same route serves `/images` and `/files`; which one you call decides where it goes.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                data:
                  type: string
                  description: 'The bytes, base64 encoded. Give this or `path`.'
                path:
                  type: string
                  description: 'A file already in the upload folder, relative to it, as `GET /media/browse` returns.'
                name:
                  type: string
                  description: The file name to store it under. Defaults to the one in the path.
                description:
                  type: string
                  description: 'The alt text for an image, or a note on a file.'
                access:
                  type: object
                  description: 'Who may see or download it, in the usual mode and groups shape.'
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      path:
                        type: string
                        description: Relative to the upload folder.
                      url:
                        type: string
                        description: 'Absolute, ready to display.'
                      ordering:
                        type: integer
                        description: 'Where it sits among the others; the first image is the one the shop shows.'
                      description:
                        type: string
                        description: 'The alt text for an image, or a note on a file.'
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Who may see or download it.
                      free_download:
                        type: boolean
                        description: 'Files only: whether it can be downloaded without buying the product.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'bad_type: The extension is not one this shop accepts.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'write_failed: The upload folder refused the file, which is usually a permissions problem.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/files/{fileId}':
    put:
      operationId: put-products-id-files-fileid
      summary: Edit an image or a file
      description: |-
        Changes the name, the description or the access of something already attached, without re-uploading
        the bytes.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
        - name: fileId
          in: path
          required: true
          description: The image or file.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: 'Any of `name`, `description`, `access` and, for a file, `free_download`. What you do not send is left alone.'
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      path:
                        type: string
                        description: Relative to the upload folder.
                      url:
                        type: string
                        description: 'Absolute, ready to display.'
                      ordering:
                        type: integer
                        description: 'Where it sits among the others; the first image is the one the shop shows.'
                      description:
                        type: string
                        description: 'The alt text for an image, or a note on a file.'
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Who may see or download it.
                      free_download:
                        type: boolean
                        description: 'Files only: whether it can be downloaded without buying the product.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such file on that product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-products-id-files-fileid
      summary: Remove an image or a file
      description: |-
        Detaches an image or a file from a product.
        
        The bytes stay in the upload folder: another product may be using the same file, and the API will
        not delete somebody's media because one product stopped pointing at it.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
        - name: fileId
          in: path
          required: true
          description: The image or file.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: What was detached.
                      deleted:
                        type: boolean
                        description: True when the row is gone.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such file on that product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/media/order':
    put:
      operationId: put-products-id-media-order
      summary: Reorder images and files
      description: |-
        Sets the order of a product's images or files. The first image is the one the shop shows, so this is
        how you choose it.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                images:
                  type: array
                  items:
                    type: integer
                  description: File ids in the order you want them.
                files:
                  type: array
                  items:
                    type: integer
                  description: The same for downloadable files.
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      images:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: integer
                              description: 'Where it sits among the others; the first image is the one the shop shows.'
                            description:
                              type: string
                              description: 'The alt text for an image, or a note on a file.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: '`all`, `none`, or `groups` when it is restricted to some.'
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                              description: Who may see or download it.
                            free_download:
                              type: boolean
                              description: 'Files only: whether it can be downloaded without buying the product.'
                        description: 'The images as they now stand, in their new order, each in the same shape as on the product.'
                      files:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            name:
                              type: string
                            path:
                              type: string
                              description: Relative to the upload folder.
                            url:
                              type: string
                              description: 'Absolute, ready to display.'
                            ordering:
                              type: integer
                              description: 'Where it sits among the others; the first image is the one the shop shows.'
                            description:
                              type: string
                              description: 'The alt text for an image, or a note on a file.'
                            access:
                              type: object
                              properties:
                                mode:
                                  type: string
                                  description: '`all`, `none`, or `groups` when it is restricted to some.'
                                groups:
                                  type: array
                                  items:
                                    type: integer
                                  description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                              description: Who may see or download it.
                            free_download:
                              type: boolean
                              description: 'Files only: whether it can be downloaded without buying the product.'
                        description: 'The files as they now stand, in the same shape as on the product.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/media/content':
    get:
      operationId: get-media-content
      summary: The bytes of an image
      description: |-
        The bytes of one image from the upload folder, so a client can edit it: crop it, rotate it, and send
        it back.
        
        This is the one route that does not answer with the envelope. It answers with the file, and with the
        cross-origin header that lets a browser read it into a canvas, which the shop's own image URLs do
        not carry. Without it, a web client cannot export an edited image at all.
        
        A path that tries to leave the upload folder is refused rather than resolved.
      tags:
        - media
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: path
          in: query
          required: true
          description: 'The image, relative to the upload folder.'
          schema:
            type: string
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties: []
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such file, or a path that tried to leave the upload folder.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/fields/{table}/{namekey}/file':
    post:
      operationId: post-fields-table-namekey-file
      summary: Upload the value of a file field
      description: |-
        Uploads the file that is the value of one of your own custom fields. Only the two field types that
        hold one accept it: `ajaximage` and `ajaxfile`.
        
        The field is named rather than numbered, and the table says which kind of record it belongs to, so
        the same route serves a product field, a category field and a customer field.
      tags:
        - fields
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: table
          in: path
          required: true
          description: 'Which kind of record: `product`, `category`, `user`, `order`, `address`.'
          schema:
            type: string
        - name: namekey
          in: path
          required: true
          description: 'The field, as `fields[].namekey` gives it.'
          schema:
            type: string
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                data:
                  type: string
                  description: 'The bytes, base64 encoded.'
                name:
                  type: string
                  description: The file name to store it under.
              required:
                - data
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      path:
                        type: string
                        description: 'Relative to the field''s own upload folder. This is the value to store on the record.'
                      name:
                        type: string
                        description: The file name it was stored under.
                      url:
                        type: string
                        description: 'Absolute, where the shop serves it from.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such record kind.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'bad_field: No such field, or it does not hold a file. bad_type: The extension is not one this shop accepts.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'write_failed: The upload folder refused the file.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}/methods':
    get:
      operationId: get-orders-id-methods
      summary: The shipping and payment methods an order can move to
      description: |-
        What this order could ship and be paid by, with what it uses now, so a change can be offered as a
        choice rather than as free text.
        
        A method is identified by its plugin and its instance together, written `plugin_id`, which is the
        pairing the backend posts back. An order that ships from several warehouses carries one method per
        shipment, and `multiple` says so.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The order.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      shipping:
                        type: object
                        properties:
                          current:
                            type: string
                            description: 'What the order uses now, as `plugin_id`. `_` when nothing is set, so the current one always matches an option.'
                          multiple:
                            type: boolean
                            description: True when the order ships in several shipments and carries a method for each.
                          options:
                            type: array
                            items:
                              type: object
                              properties:
                                value:
                                  type: string
                                  description: 'The `plugin_id` pairing to send when changing the method.'
                                label:
                                  type: string
                                  description: As the merchant named it.
                            description: What it could move to.
                          groups:
                            type: array
                            items:
                              type: object
                            description: 'For an order that ships in several shipments, the shipments and the method chosen for each. Empty otherwise.'
                        description: The shipping choice.
                      payment:
                        type: object
                        properties:
                          current:
                            type: string
                            description: What the order uses now.
                          multiple:
                            type: boolean
                            description: 'Always false: an order is paid one way.'
                          options:
                            type: array
                            items:
                              type: object
                              properties:
                                value:
                                  type: string
                                  description: 'The `plugin_id` pairing to send.'
                                label:
                                  type: string
                                  description: As the merchant named it.
                            description: What it could move to.
                        description: 'The payment choice, in the same shape.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers/{id}/addresses/{aid}/default':
    put:
      operationId: put-customers-id-addresses-aid-default
      summary: Make an address the default
      description: |-
        Marks one of a customer's addresses as their default, and answers with the customer as it now
        stands.
        
        The default is per kind: an address used for billing becomes the default billing address, and the
        one it replaces stops being it. Nothing is sent in the body.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
        - name: aid
          in: path
          required: true
          description: 'The address, from `addresses[].id`.'
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/characteristics':
    post:
      operationId: post-products-characteristics
      summary: Create a characteristic or one of its values
      description: |-
        Creates a characteristic, such as Size, or a value of one, such as XL.
        
        Send a `name` for a characteristic, or a `value` with the `parent_id` of the characteristic it
        belongs to. They are the same records at two levels, which is why one route serves both.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: 'The name of a new characteristic. Give this or `value`.'
                value:
                  type: string
                  description: 'The name of a new value, with `parent_id`.'
                parent_id:
                  type: integer
                  description: The characteristic a value belongs to.
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'What was created, which is what a variant refers to.'
                      value:
                        type: string
                        description: Its name.
                      parent_id:
                        type: integer
                        description: '`0` for a characteristic, the characteristic for a value.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_request: Neither a name nor a value was given.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/categories':
    post:
      operationId: post-products-categories
      summary: Create a product category
      description: 'Creates a category under another, or at the top of the product tree when no parent is given.'
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Its name.
                parent_id:
                  type: integer
                  description: The category to put it under. Omit for the top of the tree.
                published:
                  type: boolean
                  description: Defaults to published.
                description:
                  type: string
                  description: 'The long description, as HTML.'
                meta_description:
                  type: string
                  description: SEO description.
                access:
                  type: object
                  description: Who may see it.
                custom_fields:
                  type: object
                  description: 'Your own category fields, keyed by namekey.'
              required:
                - name
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The category that was created.
                      name:
                        type: string
                      parent_id:
                        type: integer
                        description: Where it sits.
                      published:
                        type: boolean
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/manufacturers':
    post:
      operationId: post-products-manufacturers
      summary: Create a manufacturer
      description: |-
        Creates a brand. HikaShop keeps manufacturers in the category tree under their own root, so this
        takes the same fields as a category and answers the same way.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: The brand name.
                parent_id:
                  type: integer
                  description: 'A manufacturer to nest it under, which most shops do not use.'
                published:
                  type: boolean
                  description: Defaults to published.
                description:
                  type: string
                  description: 'The long description, as HTML.'
                meta_description:
                  type: string
                  description: SEO description.
                access:
                  type: object
                  description: Who may see it.
                custom_fields:
                  type: object
                  description: Your own category fields.
              required:
                - name
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The manufacturer that was created, which is what `manufacturer_id` on a product stores.'
                      name:
                        type: string
                      parent_id:
                        type: integer
                        description: Its root.
                      published:
                        type: boolean
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_fields: One of your own fields was rejected by its own rules.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/discounts/{id}':
    get:
      operationId: get-discounts-id
      summary: Read one discount or coupon
      description: One reduction with every restriction it carries.
      tags:
        - discounts
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The discount or coupon.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      type:
                        type: string
                        description: '`discount` applies by itself, `coupon` waits for its code.'
                      code:
                        type: string
                        description: What the customer types. Empty on a discount.
                      kind:
                        type: string
                        description: Whether the value is a percentage or a fixed amount.
                      value:
                        type: number
                        description: 'The reduction, read according to `kind`.'
                      currency_id:
                        type: integer
                        description: The currency a fixed amount is in.
                      published:
                        type: boolean
                      start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which it does not apply.
                      end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it expires.
                      minimum_order:
                        type: number
                        description: 'Order total below which it does not apply, `0` for none.'
                      maximum_order:
                        type: number
                        description: 'Order total above which it stops applying, `0` for none.'
                      quota:
                        type: integer
                        description: 'How many times it may be used in total, `0` for no limit.'
                      quota_per_user:
                        type: integer
                        description: 'How many times one customer may use it, `0` for no limit.'
                      used_times:
                        type: integer
                        description: How many times it already has been.
                      tax_included:
                        type: boolean
                        description: Whether the value is understood as tax included.
                      tax_id:
                        type: integer
                        description: The tax category of the reduction itself.
                      shipping_percent:
                        type: number
                        description: A reduction on the shipping rather than on the goods.
                      minimum_products:
                        type: integer
                        description: Fewest items in the cart for it to apply.
                      maximum_products:
                        type: integer
                        description: Most items for it to still apply.
                      product_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these products. Empty means all of them.
                      exclude_product_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies to these.
                      category_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these categories.
                      category_childs:
                        type: boolean
                        description: Whether those categories include their sub-categories.
                      exclude_category_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies in these categories.
                      exclude_category_childs:
                        type: boolean
                        description: Whether those exclusions include sub-categories.
                      zone_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these zones.
                      user_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these customers.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: 'Which user groups it is for, in the usual `mode` and `groups` shape.'
                      exclude_access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Which user groups it is never for.
                      auto_load:
                        type: boolean
                        description: 'For a coupon, whether the shop applies it without the customer typing it.'
                      product_only:
                        type: boolean
                        description: Whether it reduces only the goods and leaves the fees alone.
                      discounted_products:
                        type: integer
                        description: 'How many products in the cart it applied to, on a discount that has been used.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such discount, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-discounts-id
      summary: Update a discount or a coupon
      description: |-
        Changes the fields you send and leaves the rest alone, and answers with the reduction as it now
        stands.
      tags:
        - discounts
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The discount or coupon.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              description: Any field of a discount. What you do not send keeps its value. A restriction list you do send replaces the one it had.
              additionalProperties: true
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      type:
                        type: string
                        description: '`discount` applies by itself, `coupon` waits for its code.'
                      code:
                        type: string
                        description: What the customer types. Empty on a discount.
                      kind:
                        type: string
                        description: Whether the value is a percentage or a fixed amount.
                      value:
                        type: number
                        description: 'The reduction, read according to `kind`.'
                      currency_id:
                        type: integer
                        description: The currency a fixed amount is in.
                      published:
                        type: boolean
                      start:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp before which it does not apply.
                      end:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp after which it expires.
                      minimum_order:
                        type: number
                        description: 'Order total below which it does not apply, `0` for none.'
                      maximum_order:
                        type: number
                        description: 'Order total above which it stops applying, `0` for none.'
                      quota:
                        type: integer
                        description: 'How many times it may be used in total, `0` for no limit.'
                      quota_per_user:
                        type: integer
                        description: 'How many times one customer may use it, `0` for no limit.'
                      used_times:
                        type: integer
                        description: How many times it already has been.
                      tax_included:
                        type: boolean
                        description: Whether the value is understood as tax included.
                      tax_id:
                        type: integer
                        description: The tax category of the reduction itself.
                      shipping_percent:
                        type: number
                        description: A reduction on the shipping rather than on the goods.
                      minimum_products:
                        type: integer
                        description: Fewest items in the cart for it to apply.
                      maximum_products:
                        type: integer
                        description: Most items for it to still apply.
                      product_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these products. Empty means all of them.
                      exclude_product_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies to these.
                      category_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these categories.
                      category_childs:
                        type: boolean
                        description: Whether those categories include their sub-categories.
                      exclude_category_ids:
                        type: array
                        items:
                          type: integer
                        description: Never applies in these categories.
                      exclude_category_childs:
                        type: boolean
                        description: Whether those exclusions include sub-categories.
                      zone_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these zones.
                      user_ids:
                        type: array
                        items:
                          type: integer
                        description: Restricted to these customers.
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: 'Which user groups it is for, in the usual `mode` and `groups` shape.'
                      exclude_access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Which user groups it is never for.
                      auto_load:
                        type: boolean
                        description: 'For a coupon, whether the shop applies it without the customer typing it.'
                      product_only:
                        type: boolean
                        description: Whether it reduces only the goods and leaves the fees alone.
                      discounted_products:
                        type: integer
                        description: 'How many products in the cart it applied to, on a discount that has been used.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such discount, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'code_required: A coupon needs a code, so it cannot be cleared on one. code_taken: Another coupon already uses that code. value_required: A reduction needs a value.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The shop refused to save it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-discounts-id
      summary: Delete a discount or a coupon
      description: |-
        Deletes a reduction. Orders that already used it keep the amount they were given: the discount on
        an order is a figure, not a pointer at this row.
      tags:
        - discounts
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The discount or coupon.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      deleted:
                        type: boolean
                        description: True when the row is gone.
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such discount, or the operator may not delete it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/products/{id}/files':
    post:
      operationId: post-products-id-files
      summary: Attach a downloadable file
      description: |-
        The same as attaching an image, for the files a customer downloads after buying. `free_download`
        decides whether they have to buy it first.
      tags:
        - products
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The product.
          schema:
            type: integer
      requestBody:
        required: false
        content:
          'application/json':
            schema:
              type: object
              properties:
                data:
                  type: string
                  description: 'The bytes, base64 encoded. Give this or `path`.'
                path:
                  type: string
                  description: A file already in the upload folder.
                name:
                  type: string
                  description: The file name to store it under.
                description:
                  type: string
                  description: A note on the file.
                access:
                  type: object
                  description: Who may download it.
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      name:
                        type: string
                      path:
                        type: string
                        description: Relative to the upload folder.
                      url:
                        type: string
                        description: 'Absolute, ready to display.'
                      ordering:
                        type: integer
                        description: 'Where it sits among the others; the first image is the one the shop shows.'
                      description:
                        type: string
                        description: 'The alt text for an image, or a note on a file.'
                      access:
                        type: object
                        properties:
                          mode:
                            type: string
                            description: '`all`, `none`, or `groups` when it is restricted to some.'
                          groups:
                            type: array
                            items:
                              type: integer
                            description: 'User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.'
                        description: Who may see or download it.
                      free_download:
                        type: boolean
                        description: 'Files only: whether it can be downloaded without buying the product.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such product, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'bad_type: The extension is not one this shop accepts.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'write_failed: The upload folder refused the file.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers/{id}/addresses':
    get:
      operationId: get-customers-id-addresses
      summary: A blank address form
      description: |-
        The address form of a customer: the shop's own address fields, and the values of the address asked
        for. Without an address id it is a blank form, which is what you draw to add one.
        
        The form comes with the values because a shop decides its own address fields, so build from
        `fields` rather than assuming a shape.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      address_id:
                        type: integer
                        description: 'The address this form is for, `0` for a new one.'
                      types:
                        type: array
                        items:
                          type: string
                        description: 'Which of `billing` and `shipping` it is used for.'
                      default:
                        type:
                          - string
                          - 'null'
                        description: 'Which kind it is the default for, `null` when it is not a default.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key its value is stored under.
                            type:
                              type: string
                              description: 'What to render: `text`, `radio`, `singledropdown`, `file`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save without it.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: Whether its value can be translated.
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The shop''s address fields, in display order.'
                      values:
                        type: object
                        description: 'The current values, keyed by field namekey. Empty on a blank form. Keyed by whatever address fields this shop has.'
                        additionalProperties: true
                        x-open: true
                      country_name:
                        type: string
                        description: 'The country spelled out, since the value is a zone namekey.'
                      state_name:
                        type: string
                        description: 'The state spelled out, empty where the country has none.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not see them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    post:
      operationId: post-customers-id-addresses
      summary: Add an address
      description: |-
        Writes an address of a customer. Without an address id it creates one; with one it saves that one.
        
        `types` says what the address is for, billing or shipping or both, and `default` makes it the
        default for those. An address is validated by the shop's own field rules, so a missing required
        field is refused rather than half-saved.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: 'Keyed by address field namekey, the same keys the form returned in `values`.'
                types:
                  type: array
                  items:
                    type: string
                  description: 'Which of `billing` and `shipping` this address is for. Defaults to both.'
                default:
                  type: boolean
                  description: Make it the default for its types.
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules. invalid_address: The address is not one the shop will accept, usually a missing required field.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-customers-id-addresses
      summary: Add an address
      description: |-
        Writes an address of a customer. Without an address id it creates one; with one it saves that one.
        
        `types` says what the address is for, billing or shipping or both, and `default` makes it the
        default for those. An address is validated by the shop's own field rules, so a missing required
        field is refused rather than half-saved.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: 'Keyed by address field namekey, the same keys the form returned in `values`.'
                types:
                  type: array
                  items:
                    type: string
                  description: 'Which of `billing` and `shipping` this address is for. Defaults to both.'
                default:
                  type: boolean
                  description: Make it the default for its types.
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules. invalid_address: The address is not one the shop will accept, usually a missing required field.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-customers-id-addresses
      summary: Delete an address
      description: Removes an address from a customer. Orders that used it keep their own copy of it.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/customers/{customerId}/addresses/{addressId}':
    get:
      operationId: get-customers-customerid-addresses-addressid
      summary: An address with its form
      description: |-
        The address form of a customer: the shop's own address fields, and the values of the address asked
        for. Without an address id it is a blank form, which is what you draw to add one.
        
        The form comes with the values because a shop decides its own address fields, so build from
        `fields` rather than assuming a shape.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: customerId
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
        - name: addressId
          in: path
          required: true
          description: The address.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      address_id:
                        type: integer
                        description: 'The address this form is for, `0` for a new one.'
                      types:
                        type: array
                        items:
                          type: string
                        description: 'Which of `billing` and `shipping` it is used for.'
                      default:
                        type:
                          - string
                          - 'null'
                        description: 'Which kind it is the default for, `null` when it is not a default.'
                      fields:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The key its value is stored under.
                            type:
                              type: string
                              description: 'What to render: `text`, `radio`, `singledropdown`, `file`, and the rest.'
                            raw_type:
                              type: string
                              description: 'HikaShop''s own name for the type.'
                            label:
                              type: string
                              description: 'Translated into the operator''s language.'
                            default:
                              type: string
                              description: The value used when none is given.
                            required:
                              type: boolean
                              description: Whether the shop refuses to save without it.
                            options:
                              type: array
                              items:
                                type: object
                                properties:
                                  value:
                                    type: string
                                    description: What to send back when this choice is picked.
                                  label:
                                    type: string
                                    description: What to show.
                                  label_key:
                                    type: string
                                    description: 'The translation key behind the label, when there is one.'
                              description: 'The choices, for a field that has them.'
                            multiple:
                              type: boolean
                              description: Whether more than one may be chosen.
                            translatable:
                              type: boolean
                              description: Whether its value can be translated.
                            upload_dir:
                              type: string
                              description: 'For a file field, where its uploads are kept.'
                            allowed_extensions:
                              type: string
                              description: 'For a file field, the extensions it accepts. Empty means the shop default.'
                            date_format:
                              type: string
                              description: 'For a date field, the format it is stored in.'
                        description: 'The shop''s address fields, in display order.'
                      values:
                        type: object
                        description: 'The current values, keyed by field namekey. Empty on a blank form. Keyed by whatever address fields this shop has.'
                        additionalProperties: true
                        x-open: true
                      country_name:
                        type: string
                        description: 'The country spelled out, since the value is a zone namekey.'
                      state_name:
                        type: string
                        description: 'The state spelled out, empty where the country has none.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not see them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    post:
      operationId: post-customers-customerid-addresses-addressid
      summary: Save an address
      description: |-
        Writes an address of a customer. Without an address id it creates one; with one it saves that one.
        
        `types` says what the address is for, billing or shipping or both, and `default` makes it the
        default for those. An address is validated by the shop's own field rules, so a missing required
        field is refused rather than half-saved.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: customerId
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
        - name: addressId
          in: path
          required: true
          description: The address.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: 'Keyed by address field namekey, the same keys the form returned in `values`.'
                types:
                  type: array
                  items:
                    type: string
                  description: 'Which of `billing` and `shipping` this address is for. Defaults to both.'
                default:
                  type: boolean
                  description: Make it the default for its types.
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules. invalid_address: The address is not one the shop will accept, usually a missing required field.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    put:
      operationId: put-customers-customerid-addresses-addressid
      summary: Save an address
      description: |-
        Writes an address of a customer. Without an address id it creates one; with one it saves that one.
        
        `types` says what the address is for, billing or shipping or both, and `default` makes it the
        default for those. An address is validated by the shop's own field rules, so a missing required
        field is refused rather than half-saved.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: customerId
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
        - name: addressId
          in: path
          required: true
          description: The address.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fields:
                  type: object
                  description: 'Keyed by address field namekey, the same keys the form returned in `values`.'
                types:
                  type: array
                  items:
                    type: string
                  description: 'Which of `billing` and `shipping` this address is for. Defaults to both.'
                default:
                  type: boolean
                  description: Make it the default for its types.
              required:
                - fields
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '400':
          description: 'invalid_fields: A field was rejected by its own rules. invalid_address: The address is not one the shop will accept, usually a missing required field.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
    delete:
      operationId: delete-customers-customerid-addresses-addressid
      summary: Delete an address
      description: Removes an address from a customer. Orders that used it keep their own copy of it.
      tags:
        - customers
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: customerId
          in: path
          required: true
          description: The customer.
          schema:
            type: integer
        - name: addressId
          in: path
          required: true
          description: The address.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: 'The HikaShop customer id, which is what every customer route takes.'
                      cms_id:
                        type: integer
                        description: 'The Joomla or WordPress user id, `0` for a guest with no account.'
                      name:
                        type: string
                      email:
                        type: string
                      username:
                        type: string
                        description: 'The login, empty for a guest.'
                      type:
                        type: string
                        description: '`registered` or `guest`.'
                      blocked:
                        type: boolean
                        description: Whether the CMS account is disabled.
                      can_edit_account:
                        type: boolean
                        description: 'Whether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.'
                      groups_editable:
                        type: boolean
                        description: Whether this operator may change which groups the customer is in.
                      groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                        description: The groups they are in.
                      available_groups:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            title:
                              type: string
                            assignable:
                              type: boolean
                              description: False for a group this operator may not grant.
                        description: 'Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.'
                      created:
                        type: integer
                        description: Unix timestamp of the first time the shop saw them.
                      addresses:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            types:
                              type: array
                              items:
                                type: string
                              description: 'Which of `billing` and `shipping` it is used for.'
                            name:
                              type: string
                            company:
                              type: string
                            street:
                              type: string
                            city:
                              type: string
                            post_code:
                              type: string
                            telephone:
                              type: string
                            default:
                              type: boolean
                              description: Whether it is the default for one of its types.
                            formatted:
                              type: object
                              properties:
                                text:
                                  type: string
                                  description: 'Several lines, for an invoice or a label.'
                                one_line:
                                  type: string
                                  description: 'One line, for a list.'
                              description: 'The address laid out the way this shop lays addresses out, which depends on its address format setting.'
                        description: 'Their addresses, defaults first.'
                      orders:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                            number:
                              type: string
                              description: The number the customer sees.
                            status:
                              type: string
                              description: A namekey.
                            created:
                              type: integer
                              description: Unix timestamp.
                            total:
                              type: number
                              description: 'Tax included, in the order currency.'
                            currency_id:
                              type: integer
                              description: That currency.
                        description: 'Their orders, newest first, enough to list them.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own customer fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such customer or address, or the operator may not change them.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/pair':
    post:
      operationId: post-pair
      summary: Redeem a pairing code
      description: |-
        Exchanges a single-use code, generated in **System > App Devices** of the shop's backend, for the
        device's own token. This is the only route that answers without one.
        
        The token comes back **once**. Only its hash is stored, so a lost token is re-paired, never
        recovered. Attempts are rate limited per address.
      tags:
        - pair
      x-since: '6.6.0'
      x-scopes: []
      security: []
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: 'The pairing code as shown in the backend. Case and spacing are normalised, so the grouping dash is optional.'
                device_name:
                  type: string
                  description: 'What to call this device in the device list. Defaults to `Device`.'
                platform:
                  type: string
                  description: 'A free label kept for the listing. Anything that is not a letter, a digit, a dash or an underscore is stripped.'
              required:
                - code
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      token:
                        type: string
                        description: Send this as the bearer token from now on. It is not retrievable again.
                      scopes:
                        type: array
                        items:
                          type: string
                        description: '`read`, and `write` when the code granted it.'
                      device_id:
                        type: integer
                        description: 'The row in the device list, which is what you revoke later.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_request: No code was sent.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '403':
          description: 'invalid_code: The code is unknown, already used, or expired.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '429':
          description: 'too_many_requests: Too many attempts from this address.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/orders/{id}':
    get:
      operationId: get-orders-id
      summary: Read one order
      description: |-
        The whole order: its lines, its totals, its addresses, its history and your own fields.
        
        Money is in the currency the order was placed in, which is not necessarily the shop's. Do not
        convert it: an order is a record of what was agreed at the time.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - read
      parameters:
        - name: id
          in: path
          required: true
          description: The order id.
          schema:
            type: integer
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      number:
                        type: string
                        description: The number the customer sees.
                      status:
                        type: string
                        description: A namekey.
                      created:
                        type: integer
                        description: Unix timestamp.
                      modified:
                        type: integer
                        description: Unix timestamp of the last change.
                      currency_id:
                        type: integer
                        description: The currency the order was placed in.
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: The figures. See the shape below.
                      customer:
                        type: object
                        properties:
                          name:
                            type: string
                          email:
                            type: string
                        description: Who placed it.
                      payment_method:
                        type: string
                        description: 'How it was paid, as the shop names it.'
                      shipping_method:
                        type: string
                        description: How it ships.
                      invoice_number:
                        type: string
                        description: Empty until an invoice has been issued.
                      invoice_created:
                        type:
                          - integer
                          - 'null'
                        description: Unix timestamp of the invoice.
                      items:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: integer
                              description: 'The line id, which is what `PUT /orders/{id}/products/{lineId}` takes. It is not the product id.'
                            name:
                              type: string
                              description: 'The product as it was named when ordered, which may since have changed.'
                            code:
                              type: string
                              description: Its SKU at the time.
                            quantity:
                              type: integer
                            price:
                              type: number
                              description: 'Unit price, tax excluded, as agreed at the time.'
                            tax:
                              type: number
                              description: Tax on the line.
                            editable:
                              type: boolean
                              description: 'False once the line can no longer be changed, for instance on a shipped order.'
                        description: 'The lines: `id`, `name`, `code`, `quantity`, `price`, `tax` and whether the line can still be edited.'
                      billing_address:
                        type:
                          - object
                          - 'null'
                        description: 'The address as it was at the time, `null` when there is none. It is a copy, not a pointer at the customer''s current address.'
                      shipping_address:
                        type:
                          - object
                          - 'null'
                        description: 'The same, for delivery.'
                      shipping_address_override:
                        type: boolean
                        description: Whether the delivery address was set apart from the billing one.
                      history:
                        type: array
                        items:
                          type: object
                          properties:
                            status:
                              type: string
                              description: The namekey it moved to.
                            created:
                              type: integer
                              description: Unix timestamp.
                            type:
                              type: string
                              description: 'What caused it: a payment notification, an operator, the shop itself.'
                            reason:
                              type: string
                              description: 'The note recorded with the change, when there was one.'
                            notified:
                              type: boolean
                              description: Whether the customer was emailed about it.
                        description: 'What has happened to the order, oldest first.'
                      fields:
                        type: array
                        items:
                          type: object
                        description: The definitions of your own order fields.
                      custom_fields:
                        type: object
                        description: 'Their values, keyed by namekey. The keys depend on the shop; `fields` says what they are.'
                        additionalProperties: true
                        x-open: true
                      custom_field_files:
                        type: object
                        description: 'For a field holding a file, the file behind the value. Keyed the same way.'
                        additionalProperties: true
                        x-open: true
                      fees:
                        type: object
                        properties:
                          discount:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: 'A positive figure, already subtracted from the total.'
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              code:
                                type: string
                                description: 'The coupon code, empty for a discount applied by hand.'
                            description: 'Its `amount`, its `tax`, the `tax_namekeys` behind that tax, and the coupon `code` when one was used.'
                          shipping:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that handled it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The shipping charge and what carried it.
                          payment:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that took it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The payment fee and what took it.
                        description: 'The `discount`, `shipping` and `payment` amounts, which is what `PUT /orders/{id}/fees` writes.'
                      tax_rates:
                        type: array
                        items:
                          type: object
                          properties:
                            namekey:
                              type: string
                              description: The tax rate as the shop names it.
                            rate:
                              type: number
                              description: 'As a fraction, so `0.1` is ten percent.'
                        description: 'The rates that made up the tax, each with its namekey and rate, so a total can be explained rather than only shown.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/orders/{id}/fees':
    put:
      operationId: put-orders-id-fees
      summary: Set the order fees
      description: |-
        Replaces the discount, shipping and payment amounts and re-totals the order, so a client does not
        have to compute a total itself and risk disagreeing with the shop.
        
        What you do not send keeps its current value.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order id.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                fees:
                  type: object
                  description: 'Any of `discount`, `shipping` and `payment`, each an object with at least an `amount`, tax excluded. A discount is a positive figure and is subtracted.'
              required:
                - fees
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                      fees:
                        type: object
                        properties:
                          discount:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: 'A positive figure, already subtracted from the total.'
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              code:
                                type: string
                                description: 'The coupon code, empty for a discount applied by hand.'
                            description: 'Its `amount`, its `tax`, the `tax_namekeys` behind that tax, and the coupon `code` when one was used.'
                          shipping:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that handled it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The shipping charge and what carried it.
                          payment:
                            type: object
                            properties:
                              amount:
                                type: number
                                description: Tax excluded.
                              tax:
                                type: number
                                description: The tax on it.
                              tax_namekeys:
                                type: array
                                items:
                                  type: string
                                description: Which tax rates that came from.
                              method:
                                type: string
                                description: The plugin that took it.
                              method_name:
                                type: string
                                description: As the merchant named it.
                            description: The payment fee and what took it.
                        description: 'The discount, shipping and payment amounts of the order.'
                      totals:
                        type: object
                        properties:
                          total:
                            type: number
                            description: 'What the customer owes, tax included.'
                          discount:
                            type: number
                            description: 'The discount applied, as a positive figure already subtracted.'
                          shipping:
                            type: number
                            description: The shipping charged.
                          payment:
                            type: number
                            description: The payment fee charged.
                          tax:
                            type: number
                            description: 'The tax within the total, not on top of it.'
                        description: 'The order totalled, so a client need not compute it and disagree with the shop.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '404':
          description: 'not_found: No such order, or the operator may not change it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
  '/coupons':
    get:
      operationId: get-coupons
      summary: The coupons that can be applied
      description: |-
        The shop's published coupons, so an operator can pick one rather than remember a code. Applying it
        is a separate call, and the shop validates it again there: a coupon listed here can still be
        refused for this particular order.
      tags:
        - coupons
      x-since: '6.6.0'
      x-scopes:
        - read
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: integer
                        code:
                          type: string
                          description: What the customer would type. This is what you send to apply it.
                        flat_amount:
                          type: number
                          description: 'A fixed reduction, `0` when the coupon is a percentage.'
                        percent_amount:
                          type: number
                          description: 'A percentage reduction, `0` when the coupon is a fixed amount.'
                        currency_id:
                          type: integer
                          description: The currency a flat amount is expressed in.
                        start:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp before which it is not valid.
                        end:
                          type:
                            - integer
                            - 'null'
                          description: Unix timestamp after which it expires.
                        quota:
                          type: integer
                          description: 'How many times it may be used in total, `0` for no limit.'
                        used_times:
                          type: integer
                          description: How many times it already has been.
                        minimum_order:
                          type: number
                          description: 'The order total below which it does not apply, `0` for none.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
  '/orders/{id}/status':
    post:
      operationId: post-orders-id-status
      summary: 'Change an order''s status'
      description: |-
        Moves the order and, when asked, sends the customer the same notification the backend would have
        sent.
        
        This calls the same code the backend does, so stock, invoices and every plugin listening on a
        status change behave exactly as they do there. It is not a database update.
      tags:
        - orders
      x-since: '6.6.0'
      x-scopes:
        - write
      parameters:
        - name: id
          in: path
          required: true
          description: The order id.
          schema:
            type: integer
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              type: object
              properties:
                status:
                  type: string
                  description: 'A status **namekey**, as listed by `GET /statuses`. Not the translated label.'
                notify:
                  type: boolean
                  description: 'Send the customer the notification for the new status. Defaults to `false`.'
                reason:
                  type: string
                  description: 'Recorded in the order history, and included in the notification when there is one.'
              required:
                - status
      responses:
        '200':
          description: 'The envelope, with the payload in data.'
          content:
            'application/json':
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      id:
                        type: integer
                        description: The order id.
                      status:
                        type: string
                        description: The namekey the order now has.
                      changed:
                        type: boolean
                        description: False when the order already had that status.
                      notified:
                        type: boolean
                        description: 'Whether the customer was actually emailed, which can be false even when you asked, if the status has no notification configured.'
                  meta:
                    type:
                      - object
                      - 'null'
                  error:
                    type: 'null'
        '400':
          description: 'invalid_status: No such status namekey on this shop.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '404':
          description: 'not_found: No such order, or the operator may not see it.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
        '500':
          description: 'save_failed: The order could not be saved.'
          content:
            'application/json':
              schema:
                '$ref': '#/components/schemas/Error'
