{
 "openapi": "3.1.0",
 "info": {
  "title": "OntwerpjeVlag.nl / BuildYourFlag.com storefront API",
  "version": "2026-10-09.3",
  "summary": "Read the catalogue and live prices of a custom flag printing shop, and build a cart.",
  "description": "Custom printed flags, beach flags, banners and accessories, made to order in the EU and shipped to all 27 EU countries. All read endpoints are public: no API key, no account, no sign-up (zero-auth). Prices are in EUR including VAT for the visitor's country. Shipping is free from EUR 60. Production takes 3-4 working days; delivery 5-7 working days (NL/BE) or 5-8 (rest of EU).\n\nTo buy on behalf of a user, use the Universal Commerce Protocol MCP endpoint (see /.well-known/ucp); payment always needs the buyer's approval. A design file cannot be uploaded through this API: send the buyer to the product page, where the design tool creates the print file.\n\nErrors are JSON (see the Error schema). Shopify rate-limits per IP: on HTTP 429, back off and retry later. Agent guide: /agents.md.\n\n## Versioning and deprecation\nThe Storefront GraphQL API is versioned in the URL path: /api/{version}/graphql.json, with quarterly versions named YYYY-MM. Each stable version is supported for at least 12 months. Every response carries X-Shopify-API-Version with the version that actually ran. A version close to retirement adds X-Shopify-API-Version-Warning (link to the policy); a retired version falls forward to the oldest supported one (measured 9 Oct 2026: 2025-07 is served as 2025-10 with that warning). Requests that use deprecated fields get X-Shopify-API-Deprecated-Reason. Pin a version and watch these headers. Policy: https://shopify.dev/docs/api/usage/versioning. The JSON endpoints under /products, /collections, /search and /cart are Shopify's unversioned storefront endpoints; for a stable contract use the versioned GraphQL API. This document itself is versioned by info.version.",
  "contact": {
   "name": "OntwerpjeVlag.nl / BuildYourFlag.com",
   "email": "info@ontwerpjevlag.nl",
   "url": "https://www.ontwerpjevlag.nl/pages/developers"
  },
  "termsOfService": "https://www.ontwerpjevlag.nl/policies/terms-of-service"
 },
 "servers": [
  {
   "url": "https://www.ontwerpjevlag.nl",
   "description": "Dutch store (NL)"
  },
  {
   "url": "https://www.buildyourflag.com",
   "description": "International store (EN, add /de /fr /es /it /nl)"
  }
 ],
 "externalDocs": {
  "description": "Agent guide (when to use this shop, how to order)",
  "url": "https://www.ontwerpjevlag.nl/agents.md"
 },
 "tags": [
  {
   "name": "catalog",
   "description": "Products, collections, prices and search. Public."
  },
  {
   "name": "cart",
   "description": "Session cart (cookie based). Public."
  },
  {
   "name": "storefront-api",
   "description": "Versioned Storefront GraphQL API, tokenless (no key). Public."
  },
  {
   "name": "commerce-protocol",
   "description": "Universal Commerce Protocol discovery."
  },
  {
   "name": "customer-account",
   "description": "Shopify Customer Account API, OAuth 2.0 with scopes."
  }
 ],
 "security": [
  {}
 ],
 "paths": {
  "/products.json": {
   "get": {
    "operationId": "listProducts",
    "tags": [
     "catalog"
    ],
    "summary": "List all products with variants and prices",
    "description": "Returns every published product with all variants (sizes, fabrics) and their prices incl. VAT. Paginate with page.",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 250,
       "default": 30
      },
      "description": "Products per page"
     },
     {
      "name": "page",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "default": 1
      },
      "description": "Page number"
     }
    ],
    "responses": {
     "200": {
      "description": "Products",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "products": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Product"
           }
          }
         }
        }
       }
      }
     },
     "429": {
      "description": "Rate limited, retry later",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/products/{handle}.json": {
   "get": {
    "operationId": "getProduct",
    "tags": [
     "catalog"
    ],
    "summary": "Get one product by handle",
    "description": "Full product with options, variants and prices. Handles are translated per language (www.buildyourflag.com uses English handles), so take them from listProducts or searchProducts on the same domain and language path.",
    "parameters": [
     {
      "name": "handle",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "example": "vlag-met-twee-designs-maatwerk",
      "description": "Product handle"
     }
    ],
    "responses": {
     "200": {
      "description": "Product",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "product": {
           "$ref": "#/components/schemas/Product"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "No product with this handle (empty body)"
     },
     "429": {
      "description": "Rate limited",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/collections/{handle}/products.json": {
   "get": {
    "operationId": "listCollectionProducts",
    "tags": [
     "catalog"
    ],
    "summary": "List products in a collection",
    "description": "Collections: vlaggen (flags and banners), beachflags, accessoires (poles and brackets), all. An unknown handle returns 200 with an empty products array.",
    "parameters": [
     {
      "name": "handle",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "enum": [
        "all",
        "vlaggen",
        "beachflags",
        "accessoires"
       ]
      },
      "description": "Collection handle"
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 250
      },
      "description": "Products per page"
     }
    ],
    "responses": {
     "200": {
      "description": "Products (empty array for an unknown collection)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "products": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/Product"
           }
          }
         }
        }
       }
      }
     },
     "429": {
      "description": "Rate limited",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/search/suggest.json": {
   "get": {
    "operationId": "searchProducts",
    "tags": [
     "catalog"
    ],
    "summary": "Search products by keyword",
    "description": "Keyword search over products, e.g. \"beachflag\", \"vlag\", \"flag\".",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      },
      "description": "Search terms"
     },
     {
      "name": "resources[type]",
      "in": "query",
      "schema": {
       "type": "string",
       "default": "product"
      },
      "description": "Comma-separated: product, collection, page, article"
     },
     {
      "name": "resources[limit]",
      "in": "query",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 10
      },
      "description": "Maximum results"
     }
    ],
    "responses": {
     "200": {
      "description": "Search results",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "resources": {
           "type": "object",
           "properties": {
            "results": {
             "type": "object",
             "properties": {
              "products": {
               "type": "array",
               "items": {
                "type": "object",
                "properties": {
                 "title": {
                  "type": "string"
                 },
                 "handle": {
                  "type": "string"
                 },
                 "price": {
                  "type": "string"
                 },
                 "url": {
                  "type": "string"
                 },
                 "available": {
                  "type": "boolean"
                 }
                }
               }
              }
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "422": {
      "description": "Missing q parameter (message \"Invalid parameter error\")",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/cart.js": {
   "get": {
    "operationId": "getCart",
    "tags": [
     "cart"
    ],
    "summary": "Get the current session cart",
    "description": "Cart tied to the cart cookie of this session.",
    "responses": {
     "200": {
      "description": "Cart (JSON body, served as text/javascript)",
      "content": {
       "text/javascript": {
        "schema": {
         "$ref": "#/components/schemas/Cart"
        }
       }
      }
     }
    }
   }
  },
  "/cart/add.js": {
   "post": {
    "operationId": "addToCart",
    "tags": [
     "cart"
    ],
    "summary": "Add product variants to the cart",
    "description": "Most products need a design made in the design tool on the product page; adding them without a design gives an order that cannot be printed. Use this for accessories (no design needed) or send the buyer to the product page.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "items"
        ],
        "properties": {
         "items": {
          "type": "array",
          "items": {
           "type": "object",
           "required": [
            "id",
            "quantity"
           ],
           "properties": {
            "id": {
             "type": "integer",
             "description": "Variant ID"
            },
            "quantity": {
             "type": "integer",
             "minimum": 1
            }
           }
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Added items (JSON body, served as text/javascript)",
      "content": {
       "text/javascript": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "type": "object"
           }
          }
         }
        }
       }
      }
     },
     "422": {
      "description": "Unknown variant or not enough stock; check the variant id via getProduct",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/cart/change.js": {
   "post": {
    "operationId": "changeCartLine",
    "tags": [
     "cart"
    ],
    "summary": "Change the quantity of a cart line",
    "description": "Set quantity 0 to remove the line.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "id",
         "quantity"
        ],
        "properties": {
         "id": {
          "type": "string",
          "description": "Line key from getCart"
         },
         "quantity": {
          "type": "integer",
          "minimum": 0
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Updated cart (JSON body, served as text/javascript)",
      "content": {
       "text/javascript": {
        "schema": {
         "$ref": "#/components/schemas/Cart"
        }
       }
      }
     },
     "422": {
      "description": "Unknown line or invalid quantity (\"Cannot find variant\")",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/api/{version}/graphql.json": {
   "post": {
    "operationId": "storefrontGraphql",
    "tags": [
     "storefront-api"
    ],
    "summary": "Query products, prices, collections and carts (versioned GraphQL)",
    "description": "Shopify Storefront API without an access token (tokenless). Example query: { products(first: 5) { nodes { handle title priceRange { minVariantPrice { amount currencyCode } } } } }. GraphQL errors come back as HTTP 200 with an errors array; an unknown version gives 404. Schema: https://shopify.dev/docs/api/storefront.",
    "parameters": [
     {
      "name": "version",
      "in": "path",
      "required": true,
      "description": "API version (YYYY-MM). Pin one and upgrade quarterly.",
      "schema": {
       "type": "string",
       "enum": [
        "2026-10",
        "2026-07",
        "2026-04",
        "2026-01"
       ],
       "default": "2026-10"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "query"
        ],
        "properties": {
         "query": {
          "type": "string",
          "example": "{ shop { name } }"
         },
         "variables": {
          "type": "object"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "GraphQL result; on invalid queries the body has errors instead of data",
      "headers": {
       "X-Shopify-API-Version": {
        "description": "Version that executed the request",
        "schema": {
         "type": "string"
        }
       },
       "X-Shopify-API-Version-Warning": {
        "description": "Present when the requested version is close to retirement or was retired (fell forward)",
        "schema": {
         "type": "string",
         "format": "uri"
        }
       },
       "X-Shopify-API-Deprecated-Reason": {
        "description": "Present when the request used deprecated fields",
        "schema": {
         "type": "string"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "type": [
            "object",
            "null"
           ]
          },
          "errors": {
           "type": "array",
           "items": {
            "type": "object",
            "required": [
             "message"
            ],
            "properties": {
             "message": {
              "type": "string",
              "example": "Field 'nope' doesn't exist on type 'QueryRoot'"
             },
             "locations": {
              "type": "array",
              "items": {
               "type": "object"
              }
             },
             "path": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "extensions": {
              "type": "object",
              "properties": {
               "code": {
                "type": "string",
                "example": "undefinedField"
               }
              }
             }
            }
           }
          },
          "extensions": {
           "type": "object"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Unknown or retired API version",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GraphQLError"
        }
       }
      }
     }
    }
   }
  },
  "/.well-known/ucp": {
   "get": {
    "operationId": "getUcpProfile",
    "tags": [
     "commerce-protocol"
    ],
    "summary": "Universal Commerce Protocol merchant profile",
    "description": "Supported UCP versions and the MCP endpoint (POST /api/ucp/mcp, tools such as search_catalog, create_cart, create_checkout). See https://ucp.dev.",
    "responses": {
     "200": {
      "description": "UCP profile",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/graphql": {
   "servers": [
    {
     "url": "https://shopify.com/92869165383/account/customer/api/2026-10",
     "description": "Shopify Customer Account API"
    }
   ],
   "post": {
    "operationId": "customerAccountGraphql",
    "tags": [
     "customer-account"
    ],
    "summary": "Customer orders and profile (signed-in customer)",
    "description": "GraphQL for a signed-in customer: order history, order status, addresses. Requires a customer access token.",
    "security": [
     {
      "shopifyCustomerAccount": [
       "openid",
       "email",
       "customer-account-api:full"
      ]
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "query"
        ],
        "properties": {
         "query": {
          "type": "string"
         },
         "variables": {
          "type": "object"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "GraphQL result",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "Missing or invalid token",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "schemas": {
   "Error": {
    "type": "object",
    "description": "Shopify storefront error (Content-Type application/json). An unknown product handle on /products/{handle}.json returns 404 with an empty body instead.",
    "properties": {
     "status": {
      "type": "integer",
      "example": 422
     },
     "message": {
      "type": "string",
      "example": "Cart Error"
     },
     "description": {
      "type": "string",
      "example": "Cannot find variant"
     }
    }
   },
   "GraphQLError": {
    "type": "object",
    "description": "GraphQL error list. extensions.code is machine readable (e.g. undefinedField, NOT_FOUND, THROTTLED, ACCESS_DENIED); message says what to fix.",
    "required": [
     "errors"
    ],
    "properties": {
     "errors": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "message"
       ],
       "properties": {
        "message": {
         "type": "string",
         "example": "Field 'nope' doesn't exist on type 'QueryRoot'"
        },
        "locations": {
         "type": "array",
         "items": {
          "type": "object"
         }
        },
        "path": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "extensions": {
         "type": "object",
         "properties": {
          "code": {
           "type": "string",
           "example": "undefinedField"
          }
         }
        }
       }
      }
     }
    }
   },
   "Product": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer"
     },
     "title": {
      "type": "string"
     },
     "handle": {
      "type": "string",
      "example": "vlag-met-twee-designs-maatwerk"
     },
     "body_html": {
      "type": "string",
      "description": "Product description (HTML)"
     },
     "product_type": {
      "type": "string"
     },
     "tags": {
      "description": "Tags (array in products.json, comma string in some responses)"
     },
     "variants": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Variant"
      }
     },
     "options": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "values": {
         "type": "array",
         "items": {
          "type": "string"
         }
        }
       }
      }
     },
     "images": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "src": {
         "type": "string",
         "format": "uri"
        }
       }
      }
     }
    }
   },
   "Variant": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer",
      "description": "Variant ID, use this in /cart/add.js"
     },
     "title": {
      "type": "string",
      "example": "150 × 100 cm / Standaard 115 g"
     },
     "price": {
      "type": "string",
      "description": "Price incl. VAT in EUR as a decimal string",
      "example": "49.00"
     },
     "available": {
      "type": "boolean"
     },
     "option1": {
      "type": [
       "string",
       "null"
      ]
     },
     "option2": {
      "type": [
       "string",
       "null"
      ]
     },
     "option3": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   },
   "Cart": {
    "type": "object",
    "properties": {
     "token": {
      "type": "string"
     },
     "item_count": {
      "type": "integer"
     },
     "total_price": {
      "type": "integer",
      "description": "Total in cents incl. VAT"
     },
     "currency": {
      "type": "string",
      "example": "EUR"
     },
     "items": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "key": {
         "type": "string"
        },
        "variant_id": {
         "type": "integer"
        },
        "title": {
         "type": "string"
        },
        "quantity": {
         "type": "integer"
        },
        "price": {
         "type": "integer",
         "description": "Cents"
        }
       }
      }
     }
    }
   }
  },
  "securitySchemes": {
   "shopifyCustomerAccount": {
    "type": "oauth2",
    "description": "Shopify customer accounts (OpenID Connect). Least privilege: request only the scopes you need.",
    "flows": {
     "authorizationCode": {
      "authorizationUrl": "https://shopify.com/authentication/92869165383/oauth/authorize",
      "tokenUrl": "https://shopify.com/authentication/92869165383/oauth/token",
      "scopes": {
       "openid": "Sign the customer in (OpenID Connect)",
       "email": "Read the customer e-mail address",
       "customer-account-api:full": "Read and manage the customer profile, addresses and orders",
       "customer-account-mcp-api:full": "Customer account access through the Customer Account MCP server",
       "dev.ucp.shopping.checkout:manage": "Create and manage UCP checkouts on behalf of the buyer",
       "dev.ucp.shopping.catalog.search:read": "Search the catalogue through UCP"
      }
     }
    }
   }
  }
 }
}