swagger: '2.0'
info:
  title: Questionmark API v1.2
  version: '1.2.0'
  description: |-
    [Questionmark](http://www.thequestionmark.org/)
    stands for your right to know the story behind the products you buy: where they come from
    and how they are produced. We believe that this type of transparency is crucial to developing
    sustainable supply chains and healthy products. Our mission is to make consumers more aware of
    the choices they have when buying a product and also to urge companies to become more transparent.

    The Questionmark API allows access to this information by third parties. The more consumers
    see this information, the more people will be able to make a sustainable and healthy choice,
    the more producers have an incentive to become more sustainable and healthy.

    [Lees meer](http://www.thequestionmark.org/over) over, or
    [read more](http://www.thequestionmark.org/en/questionmark-organisation) about
    Questionmark.

    ### Terms and license

    API usage is free for non-commercial use, as long as you notify us and provide proper attribution
    ([CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/)). This applies to public endpoints and
    parameters documented here, requiring no authentication.
    If you plan to make large amounts of queries (sustained >15 per minute),
    please [contact](http://thequestionmark.org/contact) us first.
    For commercial use, please contact [QM intelligence](http://www.qmintelligence.com/).

    ### Beta status

    Please note that this API has a long-running beta-status. Not all imaginable endpoints
    are present, and some parameters may be different than expected (because of historical reasons).
    With a future 2.0 version of the API, we hope to clear this up.

    This documentation is also not fully complete. Be careful when relying on undocumented features,
    as they may change without notice.

    ### Technical notes

    * The base url for the API is `https://api-c.thequestionmark.org/api/v1.2`, so e.g. the product list
      endpoint can be accessed at `https://api-c.thequestionmark.org/api/v1.2/products`.

    * Most endpoints return JSON by default, but to make sure you'll always receive JSON (also
      in case of errors), you have to either add an `Accept: application/json` header to each request,
      or append `.json` to the url (e.g. `/products.json` for the product list).

    ### Concepts

    **Products** are (food) products from the supermarket (and elsewhere) as seen by consumers, each
    receiving their own health score.

    The _health score_ has a scale consisting of `C2`, `C1`, `B1`, `B2` and `A` (with `A` the best score),
    which correspond to _Least_, _Very poor_, _Poor_, _Fair_ and _Yes_ respectively. This score is
    awarded according to the product's health group. For historical reasons, this score is sometimes called
    _personal health score_.

    [Read more about the health scoring methodology](https://www.thequestionmark.org/en/about/health).

    The _sustainability score_ and its subscores (_environment_, _social_ and _animals_) used to be a number
    on a scale of 1 to 10. These scores have been removed in favour of a new way of sustainability ranking
    that is currenty under development. For the purpose of backwards compatibility all products are considered
    to have a sustainability score of `null`. All related endpoints and parameters are **deprecated** and you are
    encouraged to stop using them.

    **Product alternatives** are similar products (currently based on usage, but that may change). Unless a specific
    order is requested by means of the `sort` parameter, "recommended" alternatives are ordered on top. Ties are
    broken using a random factor, because no brand should be recommended over another based on an arbitrary property,
    such as its name. However, this randomness breaks pagination. Therefore the `page` parameter is ignored when the
    default order is applied (i.e. it is always considered to be "1").

    **Retailers** are most importantly supermarkets. We focus on 8 of about 50, being
    [Albert Heijn](http://www.ah.nl/), [Jumbo](http://www.jumbo.com/), [Plus](http://www.plus.nl/),
    [Coop](http://www.coop.nl/), [Lidl](http://www.lidl.nl/), [Aldi](http://www.aldi.nl/),
    [Marqt](http://www.marqt.nl/) and [EkoPlaza](http://www.ekoplaza.nl/), which together
    cover about 80% of the market.

    **Usages** are the main [taxonomy](https://en.wikipedia.org/wiki/Taxonomy_%28general%29) we
    use to categorize products. Other categorizations, like the browsing categories on our
    website or health groups, are derived from the usage.
    When a product belongs to a specific usage then the product implicitly belongs to all of that usage's
    ancestors (in the hierarchy) as well.

    **Certificates** are certifications, like
    [EU Organic](http://ec.europa.eu/agriculture/organic/organic-farming/what-is-organic-farming/organic-certification_en),
    [Fairtrade](http://www.fairtrade.net/), [MSC](http://www.msc.org/) or [Vegan](https://www.vegansociety.com/), as found
    on the package of the product.

    **Rankings** are publications from Questionmark about a product category. Each contains a list of products at
    a certain point in time, ranked by sustainability and health issues. [See them here](https://www.thequestionmark.org/qm-wijzer/).

  # please note that endpoints and parameters marked with `x-internal: false` are not considered to be public

host: api-c.thequestionmark.org
schemes:
  - 'https'
basePath: /api/v1.2

produces:
  - 'application/json'

paths:
  /products:
    get:
      summary: list products
      tags:
        - Products
      parameters:
        - $ref: '#/parameters/locale'
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/detailProduct'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/sortProducts'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/package_size_min'
        - $ref: '#/parameters/package_size_max'
        - $ref: '#/parameters/package_size_unit'
        - $ref: '#/parameters/image'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              products:
                type: array
                items:
                  $ref: '#/definitions/Product'
              total:
                $ref: '#/definitions/total'
    post:
      x-internal: true
      summary: list products (same as GET but enables a client to hide parameters in the request body)
      tags:
        - Products
      parameters:
        - $ref: '#/parameters/locale'
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/detailProduct'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/sortProducts'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/package_size_min'
        - $ref: '#/parameters/package_size_max'
        - $ref: '#/parameters/package_size_unit'
        - $ref: '#/parameters/image'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              products:
                type: array
                items:
                  $ref: '#/definitions/Product'
              total:
                $ref: '#/definitions/total'
  /products/{id}:
    get:
      summary: get product by id
      tags:
        - Products
      parameters:
        - $ref: '#/parameters/idInUrl'
        - $ref: '#/parameters/detailProduct'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Product'
        404:
          description: product not found
          schema:
            $ref: '#/definitions/Error'
  /products/{barcode}:
    get:
      summary: get product by barcode
      tags:
        - Products
      parameters:
        - $ref: '#/parameters/barcodeInUrl'
        - $ref: '#/parameters/detailProduct'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Product'
        404:
          description: product not found
          schema:
            $ref: '#/definitions/Error'
  /products/{id}/alternatives:
    get:
      summary: list alternatives for a product
      tags:
        - Products
      parameters:
        - $ref: '#/parameters/idInUrl'
        - $ref: '#/parameters/locale'
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/detailProduct'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/sortProducts'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              products:
                type: array
                items:
                  $ref: '#/definitions/Product'
              total:
                $ref: '#/definitions/total'
        404:
          description: product not found
          schema:
            $ref: '#/definitions/Error'

  /brands:
    get:
      summary: list brands
      tags:
        - Brands
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q_name'
        - $ref: '#/parameters/ids'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              brands:
                type: array
                items:
                  $ref: '#/definitions/Brand'
              total:
                $ref: '#/definitions/total'
  /brands/{id}:
    get:
      summary: get brand by id
      tags:
        - Brands
      parameters:
        - $ref: '#/parameters/idInUrl'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Brand'
        404:
          description: brand not found
          schema:
            $ref: '#/definitions/Error'

  /certificates:
    get:
      summary: list certificates
      tags:
        - Certificates
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q_name'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              certificates:
                type: array
                items:
                  $ref: '#/definitions/Certificate'
              total:
                $ref: '#/definitions/total'
  /certificates/{id}:
    get:
      summary: get certificate by id
      tags:
        - Certificates
      parameters:
        - $ref: '#/parameters/idInUrl'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Certificate'
        404:
          description: certificate not found
          schema:
            $ref: '#/definitions/Error'

  /retailers:
    get:
      summary: list retailers
      tags:
        - Retailers
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q_name'
        - $ref: '#/parameters/accessible'
        - $ref: '#/parameters/include_multiproduct'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              retailers:
                type: array
                items:
                  $ref: '#/definitions/Retailer'
              total:
                $ref: '#/definitions/total'
  /retailers/{id}:
    get:
      summary: get retailer by id
      tags:
        - Retailers
      parameters:
        - $ref: '#/parameters/idInUrl'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Retailer'
        404:
          description: retailer not found
          schema:
            $ref: '#/definitions/Error'
  /regions:
    get:
      summary: list regions
      x-internal: true
      tags:
        - Regions
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              regions:
                type: array
                items:
                  $ref: '#/definitions/Region'
              total:
                $ref: '#/definitions/total'
  /regions/{id}:
    get:
      summary: get region by id
      x-internal: true
      tags:
        - Regions
      parameters:
        - $ref: '#/parameters/idInUrl'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Region'
        404:
          description: region not found
          schema:
            $ref: '#/definitions/Error'
  /health_groups:
    get:
      summary: list health groups
      x-internal: true
      tags:
        - Scoring
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              health_groups:
                type: array
                items:
                  $ref: '#/definitions/HealthGroup'
              total:
                $ref: '#/definitions/total'
  /health_groups/{id}:
    get:
      summary: get health group by id
      x-internal: true
      tags:
        - Scoring
      parameters:
        - $ref: '#/parameters/idInUrl'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/HealthGroup'
        404:
          description: health group not found
          schema:
            $ref: '#/definitions/Error'
  /categories.json:
    get:
      summary: list categories
      x-internal: true
      tags:
        - Usages
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              categories:
                type: array
                items:
                  $ref: '#/definitions/Category'

  /nutrients:
    get:
      summary: list nutrients
      tags:
        - Nutrients
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q_name'
        - $ref: '#/parameters/coded'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              nutrients:
                type: array
                items:
                  $ref: '#/definitions/Nutrient'
              total:
                $ref: '#/definitions/total'

  /products/brands:
    get:
      summary: list brands for a product query
      tags:
        - Brands
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
        - $ref: '#/parameters/brands_q_name'
        - $ref: '#/parameters/brands_ids'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              brands:
                type: array
                items:
                  $ref: '#/definitions/Brand'
              total:
                $ref: '#/definitions/total'
  /products/certificates:
    get:
      summary: list certificates for a product query
      tags:
        - Certificates
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
        - $ref: '#/parameters/certificates_q_name'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              certificates:
                type: array
                items:
                  $ref: '#/definitions/Certificate'
              total:
                $ref: '#/definitions/total'
  /products/retailers:
    get:
      summary: list retailers for a product query
      tags:
        - Retailers
      parameters:
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
        - $ref: '#/parameters/retailers_q_name'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              retailers:
                type: array
                items:
                  $ref: '#/definitions/Retailer'
              total:
                $ref: '#/definitions/total'
  /products/usages:
    get:
      summary: list usages (categories) for a product query, implicit usages included
      tags:
        - Usages
      parameters:
        - $ref: '#/parameters/usages_flat'
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
        - $ref: '#/parameters/usages_q_name'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              usages:
                type: array
                items:
                  $ref: '#/definitions/Usage'
              total:
                $ref: '#/definitions/total'

  /products/histogram:
    get:
      summary: get histogram for a product query (requires owner)
      x-internal: true
      tags:
        - Scoring
      parameters:
        - $ref: '#/parameters/histogramGroupBy'
        - $ref: '#/parameters/q'
        - $ref: '#/parameters/usage_id'
        - $ref: '#/parameters/usage_ids'
        - $ref: '#/parameters/category_id'
        - $ref: '#/parameters/brand_id'
        - $ref: '#/parameters/brand_ids'
        - $ref: '#/parameters/retailer_id'
        - $ref: '#/parameters/retailer_ids'
        - $ref: '#/parameters/health_group_id'
        - $ref: '#/parameters/health_group_present'
        - $ref: '#/parameters/certificate_id'
        - $ref: '#/parameters/certificate_ids'
        - $ref: '#/parameters/scored'
        - $ref: '#/parameters/personal_health_scored'
        - $ref: '#/parameters/score'
        - $ref: '#/parameters/nutriscore'
        - $ref: '#/parameters/nutriscores'
        - $ref: '#/parameters/personal_health_score'
        - $ref: '#/parameters/personal_health_scores'
        - $ref: '#/parameters/ids_csv'
        - $ref: '#/parameters/image'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              products_histogram:
                $ref: '#/definitions/ProductsHistogram'
        401:
          description: no authenticated owner
          schema:
            $ref: '#/definitions/Error'

  /products/{id}/sustainability_report:
    get:
      deprecated: true
      summary: get product sustainability report
      x-internal: true
      tags:
        - Products
        - Scoring
      parameters:
        - $ref: '#/parameters/idInUrl'
        - $ref: '#/parameters/locale'
      responses:
        200:
          description: success
        404:
          description: product not found
          schema:
            $ref: '#/definitions/Error'

  /rankings:
    get:
      summary: list rankings
      x-internal: true
      tags:
        - Rankings
      parameters:
        - $ref: '#/parameters/locale'
        - $ref: '#/parameters/page'
        - $ref: '#/parameters/per_page'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              rankings:
                type: array
                items:
                  $ref: '#/definitions/RankingForList'
              total:
                $ref: '#/definitions/total'
  /rankings/{id}:
    get:
      summary: get ranking by id
      x-internal: true
      tags:
        - Rankings
      parameters:
        - $ref: '#/parameters/idInUrl'
        - $ref: '#/parameters/locale'
        - $ref: '#/parameters/preview_token'
      responses:
        200:
          description: success
          schema:
            $ref: '#/definitions/Ranking'
        404:
          description: ranking not found
          schema:
            $ref: '#/definitions/Error'

  /user/sign_in:
    post:
      summary: login with email address
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/user_email'
        - $ref: '#/parameters/user_password'
        - $ref: '#/parameters/user_app'
        - $ref: '#/parameters/plan'
      responses:
        200:
          description: login failed (`success` is `false`) or succeeded
          schema:
            type: object
            properties:
              success:
                type: boolean
              error:
                description: login failure reason (only present when `success` is `false`)
                type: string
              user:
                $ref: '#/definitions/User'
  /user/status:
    get:
      summary: get user info
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/plan'
      responses:
        200:
          description: success (always)
          schema:
            type: object
            properties:
              logged_in:
                type: boolean
              user:
                $ref: '#/definitions/User'
  /user/sign_out:
    get:
      summary: logout (client needs to forget authentication_token!)
      x-internal: true
      tags:
        - Users
      responses:
        204:
          description: success
  /user:
    post:
      summary: signup by email
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/user_email'
        - $ref: '#/parameters/user_password'
        - $ref: '#/parameters/user_app'
      responses:
        200:
          description: signup failed (`success` is `false`) or succeeded
          schema:
            type: object
            properties:
              success:
                type: boolean
              errors:
                description: login failure reason (only present when `success` is `false`)
                type: array
                items:
                  type: string
              user:
                $ref: '#/definitions/User'
    put:
      summary: update user info
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/user_retailer_ids'
        # there are more owner-related parameters
      responses:
        200:
          description: update suceeded
          schema:
            type: object
            properties:
              success:
                type: boolean
              user:
                $ref: '#/definitions/User'
  /user/password/reset:
    get:
      summary: request password reset
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/email'
        - $ref: '#/parameters/app'
      responses:
        200:
          description: reset failed (`success` is `false`) or succeeded
          schema:
            type: object
            properties:
              success:
                type: boolean
  /user/invitation:
    post:
      deprecated: true
      summary: create invitation (requires owner)
      x-internal: true
      tags:
        - Users
      responses:
        200:
          description: success
    delete:
      deprecated: true
      summary: delete invitation (requires owner)
      x-internal: true
      tags:
        - Users
      responses:
        200:
          description: success
  /user/invitation/accept:
    get:
      deprecated: true
      summary: show invitation
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/invitation_token'
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              success:
                type: boolean
              user:
                $ref: '#/definitions/User'
    put:
      deprecated: true
      summary: accept invitation
      x-internal: true
      tags:
        - Users
      parameters:
        - $ref: '#/parameters/invitation_token'
        # @todo finish
      responses:
        200:
          description: success
          schema:
            type: object
            properties:
              success:
                type: boolean
              errors:
                type: object
                description: error message per user attribute (only present when `success` is `false`)
              user:
                $ref: '#/definitions/User'


parameters:
  # url parameters
  idInUrl:
    name: id
    type: integer
    in: path
    minimum: 1
    required: true
  barcodeInUrl:
    name: barcode
    type: string
    in: path
    minLength: 8
    maxLength: 13
    pattern: '[0-9]{8,13}'
    required: true

  # query parameters
  locale:
    name: locale
    type: string
    enum: ['nl', 'en']
    in: query
    description: prefer content in a specific language
    required: false
  page:
    name: page
    type: integer
    in: query
    description: page number
    minimum: 0
    default: 0
    required: false
  per_page:
    name: per_page
    type: integer
    in: query
    description: items per page
    minimum: 0
    default: 20
    maximum: 500
    required: false

  # query parameter collections
  q:
    name: q
    type: string
    in: query
    description: search query (name, brand or barcode)
    required: false
  q_name:
    name: q
    type: string
    in: query
    description: search query (name)
    required: false
  usage_id:
    name: usage_id
    type: integer
    in: query
    description: restrict by usage (category)
    required: false
  usage_ids:
    name: usage_ids[]
    type: array
    items:
      type: string
    in: query
    description: restrict to usages
    required: false
    collectionFormat: multi
  category_id:
    name: category_id
    type: integer
    in: query
    description: restrict by usage (category); deprecated in favor of `usage_id`
    x-internal: true
    required: false
  brand_id:
    name: brand_id
    type: integer
    in: query
    description: restrict to brand
    required: false
  brand_ids:
    name: brand_ids[]
    type: array
    items:
      type: integer
    in: query
    description: restrict to brands
    required: false
    collectionFormat: multi
  retailer_id:
    name: retailer_id
    type: integer
    in: query
    description: restrict to retailer
    required: false
  retailer_ids:
    name: retailer_ids[]
    type: array
    items:
      type: integer
    in: query
    description: restrict to retailers
    required: false
    collectionFormat: multi
  health_group_id:
    name: health_group_id
    type: integer
    in: query
    description: restrict to health group
    x-internal: true
    required: false
  health_group_present:
    name: health_group_present
    type: boolean
    in: query
    enum: [true]
    description: restrict to products that belong to a health group
    x-internal: true
    required: false
  certificate_id:
    name: certificate_id
    type: integer
    in: query
    description: restrict to certificate
    required: false
  certificate_ids:
    name: certificate_ids[]
    type: array
    items:
      type: integer
    in: query
    description: restrict to certificates
    required: false
    collectionFormat: multi
  scored:
    name: scored
    type: boolean
    enum: [true]
    in: query
    description: require a published health score
    required: false
  personal_health_scored:
    name: personal_health_scored
    type: boolean
    in: query
    description: require a published health score
    required: false
  score:
    name: score
    type: integer
    in: query
    description: "__deprecated__: an empty list is returned for any value"
    minimum: 1
    maximum: 10
    required: false
  nutriscore:
    x-internal: true
    name: nutriscore
    type: string
    enum: ['A', 'B', 'C', 'D', 'E']
    in: query
    description: restrict by Nutriscore
    required: false
  nutriscores:
    x-internal: true
    name: nutriscores[]
    type: array
    items:
      type: string
      enum: ['A', 'B', 'C', 'D', 'E']
    in: query
    description: restrict by Nutriscores
    required: false
    collectionFormat: multi
  personal_health_score:
    name: personal_health_score
    type: string
    enum: ['A', 'B1', 'B2', 'C1', 'C2']
    in: query
    description: restrict by health score
    required: false
  personal_health_scores:
    name: personal_health_scores[]
    type: array
    items:
      type: string
      enum: ['A', 'B1', 'B2', 'C1', 'C2']
    in: query
    description: restrict by health scores
    required: false
    collectionFormat: multi
  coded:
    name: coded
    type: boolean
    enum: [true]
    in: query
    description: restrict to nutrients having a code
    required: false
  sortProducts:
    name: sort
    type: string
    enum:
    - confirmed_at
    - confirmed_at ASC
    - name
    - name DESC
    - last_updated
    - last_updated ASC
    - nutrient_B12
    - nutrient_B12 DESC
    - nutrient_calcium
    - nutrient_calcium DESC
    - nutrient_energy
    - nutrient_energy DESC
    - nutrient_fat_saturated
    - nutrient_fat_saturated DESC
    - nutrient_fat_total
    - nutrient_fat_total DESC
    - nutrient_fat_trans
    - nutrient_fat_trans DESC
    - nutrient_fiber
    - nutrient_fiber DESC
    - nutrient_iron
    - nutrient_iron DESC
    - nutrient_natrium
    - nutrient_natrium DESC
    - nutrient_protein
    - nutrient_protein DESC
    - nutrient_salt
    - nutrient_salt DESC
    - nutrient_sugar
    - nutrient_sugar DESC
    - nutriscore
    - nutriscore DESC
    - personal_health_score
    - personal_health_score DESC
    - top_certificates_count
    - top_certificates_count DESC
    in: query
    description: sorting order, default value is "name", unless `q` is given, then the default order is determined by search rank
    required: false

  sortIngredients:
    x-internal: true
    name: sort
    type: string
    enum:
    - name
    - impact_animals
    - impact_animals_1_physical_impact
    - impact_animals_2_housing
    - impact_animals_3_feed
    - impact_animals_4_transport
    - impact_animals_5_slaughter
    - impact_animals_6_dam
    - impact_social
    - impact_social_1_association
    - impact_social_2_forced
    - impact_social_3_child
    - impact_social_4_discrimination
    - impact_social_5_wage
    - impact_social_6_health_safety
    - impact_social_7_migrant
    - impact_social_8_hours
    - impact_social_9_water
    - impact_social_10_land
    - impact_social_11_ocean
    - impact_env_2_biodiversity_1_fish_stocks
    - impact_env_2_biodiversity_2_fishery_effects
    - impact_env_2_biodiversity_3_fishery_management
    in: query
    description: sorting order (requires owner)
  sortProductIngredients:
    x-internal: true
    name: sort
    type: string
    enum:
    - name
    - name DESC
    - impact_animals
    - impact_animals_1_physical_impact
    - impact_animals_2_housing
    - impact_animals_3_feed
    - impact_animals_4_transport
    - impact_animals_5_slaughter
    - impact_animals_6_dam
    - impact_social
    - impact_social_1_association
    - impact_social_2_forced
    - impact_social_3_child
    - impact_social_4_discrimination
    - impact_social_5_wage
    - impact_social_6_health_safety
    - impact_social_7_migrant
    - impact_social_8_hours
    - impact_social_9_water
    - impact_social_10_land
    - impact_social_11_ocean
    - impact_env_2_biodiversity_1_fish_stocks
    - impact_env_2_biodiversity_2_fishery_effects
    - impact_env_2_biodiversity_3_fishery_management
    - product_count
    - product_count ASC
    in: query
    description: sorting order (requires authenticated owner with full plan)
    required: false

  ids_csv:
    name: ids
    type: string
    in: query
    description: fetch multiple products by id using a comma separated string
    required: false
  ids:
    name: ids[]
    type: array
    items:
      type: integer
    in: query
    description: fetch multiple records by id
    required: false
    collectionFormat: multi
  package_size_min:
    name: package_size_min
    type: integer
    in: query
    description: require a minimum package size (&ge;) in grams or milliliters
    required: false
  package_size_max:
    name: package_size_max
    type: integer
    in: query
    description: require a maximum package size (&le;) in grams or milliliters
    required: false
  package_size_unit:
    name: package_size_unit
    type: string
    enum: ['g', 'ml']
    in: query
    description: restrict by package size unit
    required: false
  detailProduct:
    name: detail
    x-internal: true
    type: string
    enum: ['list', 'default', 'health', 'biz_list', 'biz']
    in: query
    description: detail level (biz_list and biz require owner)
    required: false
  brands_q_name:
    name: brands[q]
    type: string
    in: query
    description: restrict by brand search query (name)
    required: false
  brands_ids:
    name: brands[ids][]
    type: array
    items:
      type: integer
    in: query
    description: restrict brands by multiple ids
    required: false
    collectionFormat: multi
  certificates_q_name:
    name: certificates[q]
    type: string
    in: query
    description: restrict by certificate search query (name)
    required: false
  retailers_q_name:
    name: retailers[q]
    type: string
    in: query
    description: restrict by retailer search query (name)
    required: false
  usages_q_name:
    name: usages[q]
    type: string
    in: query
    description: restrict by usage search query (name)
    required: false
  usages_flat:
    name: flat
    type: boolean
    in: query
    description: format the returned set of usages - which by default is arranged in a tree structure - as a flat list; pagination options only apply if this option is true
    default: false
    required: false
    x-internal: true
    # @todo Also document different responses for flat=0|1. (Hard because flat=1 is currently internal only,
    # and our client doesn't support filtering out internal responses at the moment.)
  image:
    name: image
    type: boolean
    in: query
    description: restrict by presence of image
    required: false

  histogramGroupBy:
    name: group_by
    type: string
    enum: ['personal_health_score']
    x-internal: true
    in: query
    description: x-axis of the histogram.
    required: false

  preview_token:
    name: preview_token
    type: string
    x-internal: true
    in: query
    description: secret token to preview unpublished content
    required: false

  user_email:
    name: user[email]
    type: string
    in: query
    required: true
  user_password:
    name: user[password]
    type: string
    format: password
    in: query
    required: true
  user_app:
    name: user[app]
    type: string
    enum: ['questionmark-app', 'checkit']
    in: query
    required: false
  user_retailer_ids:
    name: user[retailer_ids][]
    type: integer
    in: query
    description: preferred retailer
    required: false
  email:
    name: email
    type: string
    in: query
    required: true
  app:
    name: app
    type: string
    enum: ['questionmark-app', 'checkit']
    in: query
    required: false
  invitation_token:
    name: invitation_token
    type: string
    in: query
    required: true
  plan:
    name: plan
    type: string
    in: query
    description: require user to have this plan (comma-separated to allow one of multiple plans)
    required: false

  accessible:
    name: accessible
    type: string
    x-internal: true
    in: query
    enum: ['1', '0']
    description: only show items that are accessible to the owner (default 0 = show all)
    required: false
  include_multiproduct:
    name: include_multiproduct
    type: boolean
    x-internal: true
    in: query
    description: include the multiproduct attribute in the response
    required: false

definitions:
  # models
  Brand:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
  Retailer:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
      priority:
        type: ['integer', 'null']
        description: well-known retailers have a higher priority
  Region:
    x-internal: true
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
  Certificate:
    type: object
    description: Certificate (e.g. EU Organic, Weidemelk, Vegetarisch, etc.)
    properties:
      id:
        type: integer
      name:
        type: string
      description:
        x-internal: true
        type: string
      image_url:
        type: object
        properties:
          thumb:
            type: string
            description: url of image thumbnail
      themes:
        x-internal: true
        type: array
        description: "any of 'environment', 'social' and 'animals'"
        items:
          type: 'string'
      top:
        x-internal: true
        type: boolean
        description: "true if the certificate is among selected top certificates, read more about top certificates at https://keurmerken.milieucentraal.nl/wat-zijn-topkeurmerken/."
  ProductNutrient:
    type: object
    properties:
      name:
        type: string
        description: nutrient name
      value:
        type: number
      unit:
        type: string
        description: nutrient unit (unit / per)
  HealthGroup:
    type: object
    description: each product's health group determines how its health score is determined, based on rules
    properties:
      id:
        type: integer
      name:
        type: string
      # too unclear to show right now
      #rules:
      #  type: array
      #  items:
      #    $ref: '#/definitions/HealthGroupRule'
  HealthGroupRule:
    x-internal: true
    type: object
    description: health group rules determine a product's health score, used properties depend on its type (if you need more info, please contact us so that we can expand this)
    properties:
      type:
        type: string
        enum: ['nutrient_rule' ,'nutrient_comparison_rule', 'ingredient_absence_rule', 'static_rule']
      nutrient_code:
        type: string
      cmp:
        type: string
        enum: ['<', '≤', '>', '≥']
      value:
        type: number
      unit:
        type: string
      per:
        type: string
      score:
        type: string
        enum: ['A', 'B1', 'B2', 'C1', 'C2']
      of:
        type: string
      ingredient_absence:
        type: string
        enum: ['sugar', 'salt']
  SimpleUsage:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
  Usage:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
      children:
        type: array
        items:
          - $ref: '#/definitions/Usage'
  Category:
    x-internal: true
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
      children:
        type: array
        items:
          - $ref: '#/definitions/Category'
  Nutrient:
    type: object
    properties:
      name:
        type: string
      code:
        type: string
  Product:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
        description: product name
      brand_name:
        type: string
        description: product brand
      nutriscore:
        x-internal: true
        type: ['string', 'null']
        enum: ['A', 'B', 'C', 'D', 'E']
        description: Nutriscore
      personal_health_score:
        type: ['string', 'null']
        enum: ['A', 'B1', 'B2', 'C1', 'C2']
        description: health score
      product_nutrients:
        type: array
        items:
          - $ref: '#/definitions/ProductNutrient'
      usages:
        type: array
        description: (hierarchical) list of product usages (categories)
        items:
          - $ref: '#/definitions/SimpleUsage'
      certificates:
        type: array
        items:
          - $ref: '#/definitions/Certificate'
      retailers:
        type: array
        items:
          $ref: '#/definitions/Retailer'
      image_urls:
        type: array
        description: product image (0-1 images)
        items:
          type: object
          properties:
            thumb:
              type: string
              description: url of image thumbnail
            medium:
              type: string
              description: url of medium-size image
      barcode:
        type: string
  ProductsHistogram:
    x-internal: true
    type: object
    properties:
      data:
        type: array
        description: histogram of scores and their number of products
        items:
          type: array
          description: score-number pair
          items:
            type: ['string', 'number']

  RankingForList:
    x-internal: true
    type: object
    properties:
      id:
        type: integer
      title:
        type: string
      cover_image_url:
        type: object
        properties:
          medium:
            type: string
            description: url of medium-size cover image
          large:
            type: string
            description: url of full-size cover image
      updated_on:
        type: string
        format: date
        description: last published date (or last updated for preview)
      renews:
        description: if this ranking is a followup to another ranking, this is present and contains the ranking details
  Ranking:
    x-internal: true
    type: object
    properties:
      id:
        type: integer
      title:
        type: string
      intro:
        type: string
      disclaimer:
        type: string
      cover_image_url:
        type: object
        properties:
          medium:
            type: string
            description: url of medium-size cover image
          large:
            type: string
            description: url of full-size cover image
      sharing_image_url:
        type: string
        description: url of image used for social sharing (like an opengraph tag)
      updated_on:
        type: string
        format: date
        description: last published date (or last updated for preview)
      renews:
        $ref: '#/definitions/RankingForList'
      weights:
        type: array
        description: topics used in the ranking with their weights
        items:
          type: object
          properties:
            title:
              type: string
            value:
              type: number
              minimum: 0
              maximum: 1
              description: weight (0-1)
            theme:
              type: string
      ranking_items:
        type: array
        description: products in the ranking (best first)
        items:
          $ref: '#/definitions/RankingProduct'
      reactions:
        type: array
        description: reactions from producers
        items:
          $ref: '#/definitions/RankingReaction'
  RankingProduct:
    type: object
    properties:
      id:
        type: integer
        description: product id
      name:
        type: string
        description: product name
      image_urls:
        type: array
        description: product image (0-1 images)
        items:
          type: object
          properties:
            thumb:
              type: string
              description: url of image thumbnail
            medium:
              type: string
              description: url of medium-size image
      position:
        type: integer
        minimum: 1
        description: position in the ranking
      position_class:
        type: string
        enum: ['front', 'middle', 'back']
        description: to which ranking group the product belongs
      sustainability:
        type: object
        properties:
          score:
            type: integer
            minimum: 0
            maximum: 5
            description: sustainability score (0-5)
          best:
            type: boolean
            description: whether this product has the best sustainability score
          text:
            type: string
            description: explanation of the sustainability score (HTML)
      health:
        type: object
        properties:
          score:
            type: integer
            minimum: 0
            maximum: 5
            description: health score (0-5)
          best:
            type: boolean
            description: whether this product has the best health score
          text:
            type: string
            description: explanation of the health score (HTML, often not present)
          product_nutrients:
            type: array
            items:
              - $ref: '#/definitions/ProductNutrient'
      brand:
        $ref: '#/definitions/Brand'
      top_certificates:
        type: array
        items:
          - $ref: '#/definitions/Certificate'
  RankingReaction:
    type: object
    properties:
      text:
        type: string
      author:
        type: string

  User:
    x-internal: true
    type: object
    properties:
      id:
        type: integer
      retailer_ids:
        type: array
        description: preferred retailers
        items:
          type: integer
      email:
        type: string
        description: email address (only for email login)
      name:
        type: string
        description: name (only for owners)
      created:
        type: string
      # @todo owner fields

  total:
    type: integer
    description: total number of items
    minimum: 0

  Success:
    type: object
    properties:
      success:
        type: boolean
        description: always true
  Error:
    type: object
    properties:
      status:
        type: string
        description: error code
      error:
        type: string
        description: short human-readable error message

securityDefinitions:
  api_key:
    type: apiKey
    name: authentication_token
    in: query
