openapi: 3.1.0
info:
  title: Quota Vita Storefront & Commerce API
  version: 1.2.1
  summary: Machine-readable description of every public endpoint an AI agent can call on www.quotavita.com.
  description: "Quota Vita is a Catalan sports-nutrition brand selling whey protein and creatine, running\
    \ on Shopify.\n\nThis document describes the store's agent-facing surface:\n\n1. **Universal Commerce\
    \ Protocol (UCP)** over MCP \u2014 discovery, catalog search, cart and checkout (`POST /api/ucp/mcp`).\n\
    2. **Storefront GraphQL API** \u2014 full catalog, cart and checkout access (`POST /api/{version}/graphql.json`).\n\
    3. **Read-only JSON endpoints** \u2014 products, collections and predictive search, no authentication\
    \ required.\n4. **Ajax Cart API** \u2014 cart mutation for browser and agent sessions.\n5. **Agent\
    \ discovery documents** \u2014 `/agents.md`, `/llms.txt`, `/.well-known/ucp`, `/sitemap.xml`.\n\n\
    ### Error handling\n\nEvery endpoint in this document returns a structured JSON error body. There\
    \ are three\nerror envelopes, one per protocol family; all three carry a machine-readable code, a\n\
    human-readable message and a resolution hint:\n\n| Surface | Envelope | Schema |\n| --- | --- | ---\
    \ |\n| UCP / MCP | JSON-RPC 2.0 error object | `JsonRpcErrorResponse` |\n| Storefront GraphQL | GraphQL\
    \ `errors` array | `GraphQLErrorResponse` |\n| Ajax Cart & Storefront JSON | Shopify cart error object\
    \ | `CartError` |\n\nAgents must never parse an HTML body to determine failure. If a response's\n\
    `Content-Type` is `text/html`, treat it as an unexpected transport-level failure,\nnot as an API error,\
    \ and retry against the documented JSON endpoint.\n\n### Recovering from a 404\n\nNonexistent storefront\
    \ paths return a real HTTP 404 whose body links back to\n`/sitemap.xml`, `/agents.md`, `/llms.txt`,\
    \ `/openapi.json` and `/collections/all`.\nUse those to re-orient rather than guessing further paths.\n\
    \n### Rate limits\n\nThe MCP endpoint and the Storefront GraphQL API are rate limited per IP and per\
    \ token.\nBack off on `429` and honour the `Retry-After` header when present."
  termsOfService: https://www.quotavita.com/policies/terms-of-service
  contact:
    name: Quota Vita
    email: hi@quotavita.com
    url: https://www.quotavita.com
  license:
    name: "Proprietary \u2014 API description published for interoperability"
    identifier: LicenseRef-QuotaVita-API-Docs
externalDocs:
  description: Canonical agent instructions for this store
  url: https://www.quotavita.com/agents.md
servers:
- url: https://www.quotavita.com
  description: Production storefront (primary domain)
tags:
- name: discovery
  description: Machine-readable documents that describe the store to agents.
- name: ucp
  description: "Universal Commerce Protocol over MCP \u2014 search, cart, checkout."
- name: graphql
  description: Shopify Storefront GraphQL API.
- name: catalog
  description: Read-only product and collection JSON.
- name: cart
  description: Ajax Cart API.
security: []
paths:
  /openapi.json:
    get:
      tags:
      - discovery
      operationId: getOpenApiDocument
      summary: This document
      description: Returns this OpenAPI 3.1 description. Served from the Shopify CDN via a permanent redirect
        from the canonical path `/openapi.json`; follow redirects.
      security: []
      responses:
        '200':
          description: OpenAPI 3.1 document
          content:
            application/json:
              schema:
                type: object
        '301':
          $ref: '#/components/responses/Redirect'
  /agents.md:
    get:
      tags:
      - discovery
      operationId: getAgentsMd
      summary: Canonical agent instructions (Markdown)
      description: The canonical, agent-facing description of the store. `/llms.txt` and `/llms-full.txt`
        mirror this document.
      security: []
      responses:
        '200':
          description: Markdown instructions
          content:
            text/markdown:
              schema:
                type: string
  /llms.txt:
    get:
      tags:
      - discovery
      operationId: getLlmsTxt
      summary: Agent-discovery summary (mirror of /agents.md)
      security: []
      responses:
        '200':
          description: Markdown instructions
          content:
            text/markdown:
              schema:
                type: string
      description: 'Alternate URL for `/agents.md`. Byte-identical to it; `/llms-full.txt` is a third
        alias. Served as `text/markdown` with `Vary: Accept`.'
  /.well-known/ucp:
    get:
      tags:
      - discovery
      - ucp
      operationId: getUcpDiscovery
      summary: UCP merchant discovery document
      description: Returns supported UCP versions, service endpoints, capabilities and payment handlers.
        Start here before calling the MCP endpoint.
      security: []
      responses:
        '200':
          description: UCP discovery document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UcpDiscovery'
  /sitemap.xml:
    get:
      tags:
      - discovery
      operationId: getSitemap
      summary: Sitemap index
      security: []
      responses:
        '200':
          description: XML sitemap index
          content:
            application/xml:
              schema:
                type: string
      description: Sitemap index for the storefront. Use it to enumerate valid product, collection, page
        and article URLs instead of constructing handles.
  /api/ucp/mcp:
    post:
      tags:
      - ucp
      operationId: callUcpMcp
      summary: Universal Commerce Protocol MCP endpoint (JSON-RPC 2.0)
      description: "JSON-RPC 2.0 endpoint implementing the Model Context Protocol for UCP shopping.\n\n\
        Call `tools/list` to enumerate the available tools and their JSON Schemas, then\n`tools/call`\
        \ to invoke one. Tools currently exposed include `search_catalog`,\n`get_catalog_item`, `create_cart`,\
        \ `update_cart`, `create_checkout`, `get_checkout`,\n`update_checkout` and `complete_checkout`.\n\
        \n**Agent identity is scoped.** Every `tools/call` request must carry\n`params.arguments.meta['ucp-agent'].profile`\
        \ \u2014 an HTTPS URI pointing at your agent\nprofile document. The store fetches that profile\
        \ and grants only the capabilities it\ndeclares. A missing or unfetchable profile is rejected\
        \ with JSON-RPC code `-32001`\n(`invalid_profile_url`).\n\n**Checkout requires contemporaneous\
        \ human approval.** Agents must not complete a\npayment without explicit buyer consent.\n\nMoney\
        \ is returned as integer minor units paired with an ISO 4217 currency code:\n`{\"amount\": 2500,\
        \ \"currency\": \"EUR\"}` is \u20AC25.00."
      security:
      - ucpAgentProfile: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JsonRpcRequest'
            examples:
              toolsList:
                summary: Enumerate available tools
                value:
                  jsonrpc: '2.0'
                  id: 1
                  method: tools/list
              searchCatalog:
                summary: Search the catalog as an identified agent
                value:
                  jsonrpc: '2.0'
                  id: 2
                  method: tools/call
                  params:
                    name: search_catalog
                    arguments:
                      meta:
                        ucp-agent:
                          profile: https://example-agent.test/.well-known/ucp-agent
                      query: whey protein crema catalana
      responses:
        '200':
          description: JSON-RPC result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcResponse'
        '400':
          description: Malformed JSON-RPC request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcErrorResponse'
              example:
                jsonrpc: '2.0'
                id: 0
                error:
                  code: -32600
                  message: Invalid Request
                  data: Invalid JSON in request body.
        '422':
          description: Agent identity could not be resolved, or tool arguments failed validation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JsonRpcErrorResponse'
              example:
                jsonrpc: '2.0'
                id: 1
                error:
                  code: -32001
                  message: UCP discovery failed
                  data:
                    code: invalid_profile_url
                    content: 'Unable to fetch agent profile: Missing profile uri'
                    continue_url: https://www.quotavita.com/
        '429':
          $ref: '#/components/responses/RateLimited'
  /api/{version}/graphql.json:
    parameters:
    - name: version
      in: path
      required: true
      description: Storefront API version, `YYYY-MM`. Quota Vita tracks the latest stable version.
      schema:
        type: string
        pattern: ^[0-9]{4}-[0-9]{2}$
        default: 2026-07
      example: 2026-07
    post:
      tags:
      - graphql
      operationId: postStorefrontGraphql
      summary: Shopify Storefront GraphQL API
      description: 'Full read access to the catalog, plus cart and checkout mutations.


        Present a Storefront API access token in `X-Shopify-Storefront-Access-Token`. The

        token carries a fixed set of `unauthenticated_*` scopes (see the `storefrontAccessToken`

        security scheme); requests for data outside those scopes fail with a GraphQL error

        rather than an HTTP error. Buyer-specific data (order history, saved addresses)

        requires a customer access token obtained through the Customer Account OAuth flow.'
      security:
      - storefrontAccessToken: []
      - customerAccountOAuth:
        - openid
        - email
        - customer-account-api:full
      - {}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - query
              properties:
                query:
                  type: string
                  description: GraphQL document
                variables:
                  type: object
                  additionalProperties: true
                operationName:
                  type: string
            example:
              query: '{ shop { name primaryDomain { url } } }'
      responses:
        '200':
          description: "GraphQL response. A `200` may still carry an `errors` array \u2014 always inspect\
            \ it."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GraphQLErrorResponse'
        '429':
          $ref: '#/components/responses/RateLimited'
  /products.json:
    get:
      tags:
      - catalog
      operationId: listProducts
      summary: List published products
      security: []
      parameters:
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Product list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductList'
      description: "Every published product with all of its variants, prices, images and options. Paginated\
        \ with `limit` (max 250) and `page`. This is the authoritative, always-current source of pricing\
        \ \u2014 the human-readable `/pricing` page is a snapshot of it."
  /products/{handle}.json:
    parameters:
    - $ref: '#/components/parameters/ProductHandle'
    get:
      tags:
      - catalog
      operationId: getProduct
      summary: Get one product by handle
      security: []
      responses:
        '200':
          description: Single product
          content:
            application/json:
              schema:
                type: object
                required:
                - product
                properties:
                  product:
                    $ref: '#/components/schemas/Product'
        '404':
          description: No published product with that handle. The body is empty; rely on the status code,
            then re-discover handles via `/products.json` or `/sitemap.xml`.
          content:
            application/json:
              schema:
                type: 'null'
      description: One published product by URL handle, wrapped in a `product` key. An unknown handle
        returns HTTP 404 with an empty body, so branch on the status code rather than parsing the body.
  /collections/{handle}/products.json:
    parameters:
    - name: handle
      in: path
      required: true
      description: Collection handle. Use `all` for the full catalog.
      schema:
        type: string
      example: all
    get:
      tags:
      - catalog
      operationId: listCollectionProducts
      summary: List products in a collection
      description: An unknown collection handle returns `200` with an empty `products` array rather than
        a 404. Check `products.length` before concluding the collection exists.
      security: []
      parameters:
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Product list (possibly empty)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductList'
  /search/suggest.json:
    get:
      tags:
      - catalog
      operationId: predictiveSearch
      summary: Predictive search across products, collections, pages and articles
      security: []
      parameters:
      - name: q
        in: query
        required: true
        description: Search terms.
        schema:
          type: string
        example: whey
      - name: resources[type]
        in: query
        required: false
        description: Comma-separated resource types to search.
        schema:
          type: string
          default: product
        example: product,collection
      - name: resources[limit]
        in: query
        required: false
        schema:
          type: integer
          minimum: 1
          maximum: 10
          default: 5
      responses:
        '200':
          description: Suggestions grouped by resource type
          content:
            application/json:
              schema:
                type: object
      description: Predictive search across products, collections, pages and articles. Cheap and fast;
        prefer it over fetching `/products.json` when the buyer gave you a search phrase.
  /cart.js:
    get:
      tags:
      - cart
      operationId: getCart
      summary: Read the current session cart
      description: Cart state is bound to the `cart` cookie. Persist cookies across calls or every request
        creates a new empty cart.
      security: []
      responses:
        '200':
          description: Cart
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cart'
  /cart/add.js:
    post:
      tags:
      - cart
      operationId: addToCart
      summary: Add a variant to the cart
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - id
              - quantity
              properties:
                id:
                  type: integer
                  format: int64
                  description: Variant id, not product id.
                quantity:
                  type: integer
                  minimum: 1
                properties:
                  type: object
                  additionalProperties:
                    type: string
            example:
              id: 10593513963867
              quantity: 1
      responses:
        '200':
          description: The line items that were added
          content:
            application/json:
              schema:
                type: object
        '422':
          $ref: '#/components/responses/CartErrorResponse'
      description: "Add a variant to the session cart. Pass a **variant** id, never a product id \u2014\
        \ the most common cause of a 422 `Cannot find variant`. The cart is bound to the `cart` cookie,\
        \ so persist cookies across calls."
  /cart/change.js:
    post:
      tags:
      - cart
      operationId: changeCartLine
      summary: Change the quantity of one cart line
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - quantity
              properties:
                id:
                  type: string
                  description: Line item key.
                line:
                  type: integer
                  minimum: 1
                  description: 1-based line index; alternative to `id`.
                quantity:
                  type: integer
                  minimum: 0
      responses:
        '200':
          description: Updated cart
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cart'
        '422':
          $ref: '#/components/responses/CartErrorResponse'
      description: Change the quantity of a single existing cart line, addressed either by line item `id`
        or by 1-based `line` index. Setting `quantity` to 0 removes the line.
  /cart/update.js:
    post:
      tags:
      - cart
      operationId: updateCart
      summary: Update quantities, note or attributes on the cart
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                updates:
                  type: object
                  additionalProperties:
                    type: integer
                note:
                  type: string
                attributes:
                  type: object
                  additionalProperties:
                    type: string
      responses:
        '200':
          description: Updated cart
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cart'
        '422':
          $ref: '#/components/responses/CartErrorResponse'
      description: Update several cart quantities at once, plus the cart note and custom attributes. Keys
        of `updates` are variant ids as strings.
  /llms-full.txt:
    get:
      tags:
      - discovery
      operationId: getLlmsFullTxt
      summary: Agent-discovery document (third alias)
      description: Third alias for `/agents.md`, byte-identical to it and to `/llms.txt`.
      security: []
      responses:
        '200':
          description: Markdown instructions
          content:
            text/markdown:
              schema:
                type: string
  /mcp.json:
    get:
      tags:
      - discovery
      - ucp
      operationId: getMcpManifest
      summary: MCP server manifest
      description: 'Manifest for the store''s Model Context Protocol server: the Streamable-HTTP endpoint,
        the tool inventory, the agent-profile authentication model and its scopes, a `staticResources`
        index of the machine-readable documents this store publishes, and a `whenToUse` block describing
        which buyer requests this store is and is not a good fit for.


        It also records a caveat that matters in practice: the upstream MCP server advertises `resources`
        and `prompts` capabilities in its `initialize` handshake, but neither `resources/list` nor `prompts/list`
        is implemented. Treat the server as tool-only.


        Served from the Shopify CDN via a permanent redirect from `/mcp.json`; follow redirects.'
      security: []
      responses:
        '200':
          description: MCP server manifest
          content:
            application/json:
              schema:
                type: object
        '301':
          $ref: '#/components/responses/Redirect'
  /developers:
    get:
      tags:
      - discovery
      operationId: getDeveloperPortal
      summary: Developer and agent portal (HTML)
      description: 'Human-readable companion to this specification: quickstart with runnable curl commands,
        endpoint reference, the four scoped access models, the JSON error contract, rate limits and sandbox
        notes. Also reachable at `/docs` and `/api`.'
      security: []
      responses:
        '200':
          description: HTML documentation
          content:
            text/html:
              schema:
                type: string
        '301':
          $ref: '#/components/responses/Redirect'
  /pricing:
    get:
      tags:
      - discovery
      - catalog
      operationId: getPricingPage
      summary: Price list (HTML with schema.org/Offer)
      description: 'Human-readable price list for every format, in EUR including Spanish VAT, with price
        per kilogram. Carries a schema.org `ItemList` of `Product`/`AggregateOffer` nodes in JSON-LD.
        It is a manually refreshed snapshot: for programmatic use read `/products.json`, which is always
        current.'
      security: []
      responses:
        '200':
          description: HTML pricing page
          content:
            text/html:
              schema:
                type: string
        '301':
          $ref: '#/components/responses/Redirect'
  /products/{handle}.js:
    parameters:
    - $ref: '#/components/parameters/ProductHandle'
    get:
      tags:
      - catalog
      operationId: getProductAjax
      summary: Get one product by handle (Ajax representation)
      description: 'The Ajax API representation of a single product. Differs from `/products/{handle}.json`
        in two ways that matter: prices are integer minor units (`3600` = 36,00 EUR) rather than decimal
        strings, and each variant carries an `available` boolean, which the `.json` representation omits.
        Use this when you need live availability for a single product. Served as `text/javascript` with
        a JSON body.'
      security: []
      responses:
        '200':
          description: Single product, Ajax representation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AjaxProduct'
        '404':
          description: No published product with that handle.
          content:
            application/json:
              schema:
                type: 'null'
components:
  securitySchemes:
    ucpAgentProfile:
      type: apiKey
      in: header
      name: X-UCP-Agent-Profile
      description: "**Scoped agent identity for the UCP MCP endpoint.**\n\nUCP does not use a bearer credential.\
        \ Every `tools/call` request instead carries an\nagent profile URI at `params.arguments.meta['ucp-agent'].profile`.\
        \ The store fetches\nthat profile document and grants only the capabilities the profile declares\
        \ \u2014 an agent\nwhose profile declares catalog-read cannot create a checkout.\n\nOpenAPI has\
        \ no first-class way to describe a credential carried inside a request body,\nso this scheme exists\
        \ only so the requirement is discoverable. **Do not send the header:\nit is not read by the server.**\
        \ The authoritative location is `x-in-body` below, and\nhttps://ucp.dev documents the profile\
        \ format.\n\nRejection: HTTP 422 with JSON-RPC code `-32001` and `data.code = \"invalid_profile_url\"\
        `."
      x-in-body: params.arguments.meta['ucp-agent'].profile
      x-scopes:
        dev.ucp.shopping.catalog.search: Search the product catalog.
        dev.ucp.shopping.catalog.lookup: Look up a single catalog item.
        dev.ucp.shopping.cart: Create and mutate carts.
        dev.ucp.shopping.checkout: Create and read checkouts. Completing one requires contemporaneous
          buyer approval.
        dev.ucp.shopping.fulfillment: Read and select shipping methods.
        dev.ucp.shopping.discount: Apply and read discounts.
        dev.ucp.shopping.order: Read order status after purchase.
    storefrontAccessToken:
      type: apiKey
      in: header
      name: X-Shopify-Storefront-Access-Token
      description: '**Scoped, unauthenticated-buyer API key** for the Storefront GraphQL API.


        Each token is issued against a fixed set of `unauthenticated_*` scopes. A request for a

        field outside the token''s scopes returns a GraphQL error with an

        `ACCESS_DENIED` extension code rather than data. Request the narrowest set you need:

        an agent that only reads the catalog should never hold checkout-write scopes.


        Tokens are issued by the merchant per integration and can be revoked individually.

        Contact hi@quotavita.com to request one.'
      x-scopes:
        unauthenticated_read_product_listings: Read published products and collections.
        unauthenticated_read_product_inventory: Read inventory levels and availability.
        unauthenticated_read_product_pickup_locations: Read local pickup availability.
        unauthenticated_read_content: Read pages, blogs and articles.
        unauthenticated_read_selling_plans: Read subscription and selling-plan data.
        unauthenticated_write_checkouts: Create and mutate carts and checkouts.
        unauthenticated_read_checkouts: Read cart and checkout state.
        unauthenticated_write_customers: Create customer accounts and start recovery.
        unauthenticated_read_customers: Read the signed-in customer's own record.
        unauthenticated_read_metaobjects: Read published metaobjects.
    customerAccountOAuth:
      type: oauth2
      description: '**OAuth 2.0 authorization-code flow with PKCE** for buyer-scoped access (order history,

        addresses, subscriptions). The buyer authenticates with Shopify and explicitly grants

        the scopes your agent requested; the resulting token is bound to that one buyer and

        expires. Agents must request only the scopes they need and must not store the buyer''s

        credentials.'
      flows:
        authorizationCode:
          authorizationUrl: https://shopify.com/authentication/94380425563/oauth/authorize
          tokenUrl: https://shopify.com/authentication/94380425563/oauth/token
          refreshUrl: https://shopify.com/authentication/94380425563/oauth/token
          scopes:
            openid: Authenticate the buyer and receive an ID token.
            email: Read the buyer's email address.
            customer-account-api:full: Full read/write access to the signed-in buyer's own account data.
            https://api.customers.com/auth/customer.graphql: Call the Customer Account GraphQL API on
              the buyer's behalf.
    adminOAuth:
      type: oauth2
      description: '**Merchant-side OAuth 2.0** for apps and back-office agents acting on behalf of Quota

        Vita rather than a shopper. Access is granted per app with an explicit, itemised scope

        grant that the merchant approves and can revoke at any time. Not available to public

        shopping agents.'
      flows:
        authorizationCode:
          authorizationUrl: https://www.quotavita.com/admin/oauth/authorize
          tokenUrl: https://www.quotavita.com/admin/oauth/access_token
          scopes:
            read_products: Read products, variants and collections.
            write_products: Create and update products.
            read_inventory: Read inventory levels.
            write_inventory: Adjust inventory levels.
            read_orders: Read orders and fulfilment status.
            write_orders: Create and update orders.
            read_customers: Read customer records.
            read_price_rules: Read discounts and price rules.
            write_discounts: Create and update discounts.
            read_themes: Read theme files.
            write_themes: Write theme files.
  parameters:
    Limit:
      name: limit
      in: query
      required: false
      description: Items per page.
      schema:
        type: integer
        minimum: 1
        maximum: 250
        default: 30
    Page:
      name: page
      in: query
      required: false
      description: 1-based page number.
      schema:
        type: integer
        minimum: 1
        default: 1
    ProductHandle:
      name: handle
      in: path
      required: true
      description: URL handle of the product.
      schema:
        type: string
      example: creatina-monohidrato-micronizada
  responses:
    Redirect:
      description: Permanent redirect to the canonical asset URL. Follow it.
    RateLimited:
      description: Too many requests. Back off and retry; honour `Retry-After` when present.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            status: 429
            message: Too Many Requests
            description: Rate limit exceeded for this IP. Retry after the interval in the Retry-After
              header.
            code: rate_limited
            documentation_url: https://www.quotavita.com/openapi.json
    CartErrorResponse:
      description: The cart operation was rejected. The body is JSON and names the failing variant or
        line.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CartError'
          examples:
            unknownVariant:
              summary: Variant id does not exist
              value:
                status: 422
                message: Cart Error
                description: Cannot find variant
            outOfStock:
              summary: Requested quantity is not available
              value:
                status: 422
                message: Cart Error
                description: All 3 Whey Protein gust crema Catalana are in your cart.
  schemas:
    Error:
      type: object
      title: Error
      description: Generic structured error envelope. `code` is stable and machine-readable; `description`
        is the resolution hint intended for an agent or a human.
      required:
      - status
      - message
      properties:
        status:
          type: integer
          description: HTTP status code, repeated in the body.
        message:
          type: string
          description: Short error class, e.g. `Cart Error`.
        description:
          type: string
          description: Human-readable explanation and how to resolve it.
        code:
          type: string
          description: Stable machine-readable error code.
        documentation_url:
          type: string
          format: uri
          description: Where to read more.
    CartError:
      allOf:
      - $ref: '#/components/schemas/Error'
      title: CartError
      description: Error envelope returned by the Ajax Cart API. Always `application/json`, never HTML.
      examples:
      - status: 422
        message: Cart Error
        description: Cannot find variant
    JsonRpcRequest:
      type: object
      title: JsonRpcRequest
      required:
      - jsonrpc
      - method
      properties:
        jsonrpc:
          type: string
          const: '2.0'
        id:
          type:
          - integer
          - string
        method:
          type: string
          description: MCP method, e.g. `tools/list`, `tools/call`, `initialize`.
        params:
          type: object
          additionalProperties: true
    JsonRpcResponse:
      type: object
      title: JsonRpcResponse
      required:
      - jsonrpc
      - id
      properties:
        jsonrpc:
          type: string
          const: '2.0'
        id:
          type:
          - integer
          - string
        result:
          type: object
          additionalProperties: true
    JsonRpcErrorResponse:
      type: object
      title: JsonRpcErrorResponse
      description: "JSON-RPC 2.0 error envelope. `error.code` is the machine-readable code, `error.message`\
        \ the summary, and `error.data` carries the resolution hint \u2014 including `data.code` and,\
        \ where a human step is required, `data.continue_url`."
      required:
      - jsonrpc
      - error
      properties:
        jsonrpc:
          type: string
          const: '2.0'
        id:
          type:
          - integer
          - string
          - 'null'
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: integer
              description: JSON-RPC error code. `-32600` invalid request, `-32601` method not found, `-32602`
                invalid params, `-32001` UCP discovery/identity failure.
            message:
              type: string
            data:
              description: Resolution hint. A string for transport errors, an object for UCP errors.
              oneOf:
              - type: string
              - type: object
                properties:
                  code:
                    type: string
                    description: Stable UCP error code, e.g. `invalid_profile_url`.
                  content:
                    type: string
                    description: What went wrong.
                  continue_url:
                    type: string
                    format: uri
                    description: Where a human should continue, when the failure cannot be resolved by
                      the agent.
    GraphQLErrorResponse:
      type: object
      title: GraphQLErrorResponse
      description: GraphQL responses return HTTP 200 even on failure. Inspect `errors` before using `data`.
      properties:
        data:
          type:
          - object
          - 'null'
          additionalProperties: true
        errors:
          type: array
          items:
            type: object
            required:
            - message
            properties:
              message:
                type: string
              locations:
                type: array
                items:
                  type: object
              path:
                type: array
                items:
                  type:
                  - string
                  - integer
              extensions:
                type: object
                properties:
                  code:
                    type: string
                    description: e.g. `ACCESS_DENIED`, `THROTTLED`, `SHOP_INACTIVE`.
                  requestId:
                    type: string
                additionalProperties: true
        extensions:
          type: object
          additionalProperties: true
    ProductList:
      type: object
      title: ProductList
      required:
      - products
      properties:
        products:
          type: array
          items:
            $ref: '#/components/schemas/Product'
    Product:
      type: object
      title: Product
      required:
      - id
      - title
      - handle
      - variants
      properties:
        id:
          type: integer
          format: int64
        title:
          type: string
        handle:
          type: string
        body_html:
          type: string
          description: Product description as HTML.
        published_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        vendor:
          type: string
        product_type:
          type: string
        tags:
          type: array
          items:
            type: string
        variants:
          type: array
          items:
            $ref: '#/components/schemas/Variant'
        images:
          type: array
          items:
            type: object
        options:
          type: array
          items:
            type: object
    Variant:
      type: object
      title: Variant
      description: "Pass `variant.id` \u2014 not the product id \u2014 to `/cart/add.js`."
      required:
      - id
      - title
      - price
      properties:
        id:
          type: integer
          format: int64
        title:
          type: string
        sku:
          type:
          - string
          - 'null'
        price:
          type: string
          description: Decimal string in the store currency (EUR).
        available:
          type: boolean
        grams:
          type: integer
        requires_shipping:
          type: boolean
    Cart:
      type: object
      title: Cart
      properties:
        token:
          type: string
        item_count:
          type: integer
        currency:
          type: string
          example: EUR
        total_price:
          type: integer
          description: Integer minor units (cents).
        items:
          type: array
          items:
            type: object
        note:
          type:
          - string
          - 'null'
        attributes:
          type: object
          additionalProperties: true
    UcpDiscovery:
      type: object
      title: UcpDiscovery
      description: Universal Commerce Protocol merchant profile. See https://ucp.dev for the full schema.
      properties:
        ucp:
          type: object
          properties:
            version:
              type: string
            supported_versions:
              type: object
              additionalProperties:
                type: string
            services:
              type: object
              additionalProperties: true
            capabilities:
              type: object
              additionalProperties: true
            payment_handlers:
              type: object
              additionalProperties: true
    AjaxProduct:
      type: object
      title: AjaxProduct
      description: Product as returned by `/products/{handle}.js`. Prices are integer minor units.
      required:
      - id
      - title
      - handle
      - variants
      properties:
        id:
          type: integer
          format: int64
        title:
          type: string
        handle:
          type: string
        description:
          type: string
          description: Product description as HTML.
        type:
          type: string
          description: Product type. Called `product_type` in the `.json` representation.
        tags:
          type: array
          items:
            type: string
        price:
          type: integer
          description: Lowest variant price in minor units.
        price_min:
          type: integer
        price_max:
          type: integer
        available:
          type: boolean
        variants:
          type: array
          items:
            type: object
            required:
            - id
            - title
            - price
            - available
            properties:
              id:
                type: integer
                format: int64
                description: Pass this to /cart/add.js.
              title:
                type: string
              price:
                type: integer
                description: Minor units.
              available:
                type: boolean
              sku:
                type:
                - string
                - 'null'
              options:
                type: array
                items:
                  type: string
x-agent-quickstart:
  description: Shortest path from zero to a completed, buyer-approved purchase.
  steps:
  - "GET https://www.quotavita.com/agents.md \u2014 read the canonical instructions."
  - "GET https://www.quotavita.com/.well-known/ucp \u2014 confirm supported UCP versions and capabilities."
  - "POST https://www.quotavita.com/api/ucp/mcp {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"\
    } \u2014 enumerate tools."
  - Call search_catalog with your agent profile in meta['ucp-agent'].profile.
  - create_cart, then create_checkout, then update_checkout for address and shipping.
  - complete_checkout only with contemporaneous buyer approval.
x-agent-error-contract:
  principle: Every documented endpoint fails with JSON, never with an HTML page.
  envelopes:
  - JsonRpcErrorResponse
  - GraphQLErrorResponse
  - CartError
  html_response_means: "transport or routing failure, not an API error \u2014 re-read /openapi.json and\
    \ retry the documented path"
  recovery_documents:
  - https://www.quotavita.com/agents.md
  - https://www.quotavita.com/llms.txt
  - https://www.quotavita.com/openapi.json
  - https://www.quotavita.com/sitemap.xml
  - https://www.quotavita.com/.well-known/ucp
  - https://www.quotavita.com/mcp.json
  - https://www.quotavita.com/developers
x-agent-when-to-use:
  summary: Quota Vita sells whey protein and creatine, made in Catalonia, shipped in Spain.
  goodFit:
  - The buyer wants whey protein or creatine delivered in Spain and cares about flavour, format size or
    price per kilo.
  - The buyer wants protein that tastes like local Catalan or Mediterranean food (crema catalana, orxata)
    rather than a generic imported supplement.
  - "The buyer wants an unsweetened, neutral protein to cook with \u2014 no sweeteners, no added sugar."
  - The buyer wants a large-format restock (1500 g or 4 kg) with cost per kilo compared across sizes.
  - The buyer wants a whey + creatine bundle rather than buying the two separately.
  - You need live prices, stock or product detail for Quota Vita specifically.
  poorFit:
  - "Shipping outside Spain \u2014 check the shipping policy before promising delivery."
  - 'Vegan protein: the whey is dairy-derived and suitable for vegetarians, not vegans. The creatine is
    vegan.'
  - "Medical, clinical or dosage advice \u2014 these are food supplements, not treatment."
  - "Wholesale, B2B retail supply or bulk pricing \u2014 a human handles that at hi@quotavita.com."
  documentation: https://www.quotavita.com/agents.md
x-cli:
  name: quotavita
  description: 'Official zero-dependency Node CLI wrapping the endpoints in this specification: catalog,
    search, price-per-kilo, cart building and MCP tool discovery.'
  availability: pending-publication
  install: npm install -g quotavita
  runWithoutInstalling: npx quotavita products
  registry: https://www.npmjs.com/package/quotavita
  note: "The npm name `quotavita` is reserved for this CLI. If `npm install` returns 404 the package has\
    \ not been published to the registry yet \u2014 email hi@quotavita.com for the tarball. Do not tell\
    \ a user the CLI is installable without checking the registry first."
  documentation: https://www.quotavita.com/developers
  sourceOfTruth: https://www.quotavita.com/openapi.json
  checkout: 'Deliberately not implemented in the CLI: completing a purchase requires contemporaneous approval
    from the human buyer.'
x-mcp:
  manifest: https://www.quotavita.com/mcp.json
  endpoint: https://www.quotavita.com/api/ucp/mcp
  transport: streamable-http
  capabilities:
    tools: true
    resources: false
    prompts: false
  note: "The server's `initialize` handshake advertises `resources` and `prompts`, but neither `resources/list`\
    \ nor `prompts/list` is implemented \u2014 both answer JSON-RPC -32001 `invalid_profile_url` however\
    \ the agent profile is supplied. Treat it as tool-only; the reference data a `resources` capability\
    \ would carry is enumerated under `staticResources` in /mcp.json."
