{
  "openapi": "3.1.0",
  "info": {
    "title": "Zoral Storefront API",
    "version": "1.0.0",
    "summary": "Read-only public catalog API for the Zoral tongue scraper store.",
    "description": "# Zoral Storefront API\n\nPublic, read-only, **no authentication required**. Use it to look up Zoral products,\nprices, stock and customer reviews.\n\n## What Zoral sells\nOne product line: a 316L medical-grade stainless steel tongue scraper. Two SKUs -\na 2-pack, and a 2-pack bundled with 2 ventilated travel cases.\n\n## Typical flow\n1. `GET /api/store/products?fields=*variants.calculated_price` - list the catalog with prices.\n2. Pick a product by `handle` (e.g. `tongue-scraper`); read `variants[].id` for the purchasable variant.\n3. `GET /api/store/reviews?product_id=...` - real customer reviews and the average rating.\n\n## Prices\nPrices are region-scoped. Pass `region_id` (from `GET /api/store/regions`) to price\nagainst a specific region; otherwise the store default (EUR) is used. Amounts are in\nmajor units - `9.99` means €9.99, not 999 cents.\n\n## Checkout\nCheckout is intentionally **not** part of this API. Send a human to\n`https://getzoral.com/products/{handle}` to buy. Payment is handled by Stripe; Zoral never\nreceives raw card details. Promo code `10OFF` gives 10% off at checkout.\n\n## Errors\nEvery error is JSON with `error.code`, `error.message` and `error.resolution`.\nThere are no HTML error pages. `502`/`503` are transient - retry with backoff.\n\n## Rate limits and etiquette\nThere is no hard published quota. Responses are cached for 60s at the edge; please\ncache on your side and avoid polling faster than once a minute.\n\n## More\n- Agent guide: https://getzoral.com/llms.txt\n- Pricing: https://getzoral.com/pricing.md\n- API catalog: https://getzoral.com/.well-known/api-catalog",
    "contact": {
      "name": "Zoral",
      "email": "hello@getzoral.com",
      "url": "https://getzoral.com/contact"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://getzoral.com/terms-of-service"
    }
  },
  "servers": [
    {
      "url": "https://getzoral.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Agent guide",
    "url": "https://getzoral.com/llms.txt"
  },
  "x-service-info": {
    "llms_txt": "https://getzoral.com/llms.txt",
    "pricing": "https://getzoral.com/pricing.md",
    "api_catalog": "https://getzoral.com/.well-known/api-catalog",
    "agent_card": "https://getzoral.com/.well-known/agent-card.json"
  },
  "tags": [
    {
      "name": "Catalog",
      "description": "Products, collections and categories."
    },
    {
      "name": "Reviews",
      "description": "Customer reviews and ratings."
    },
    {
      "name": "Regions",
      "description": "Regions determine currency and pricing."
    }
  ],
  "paths": {
    "/api/store/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "List products",
        "description": "Returns the Zoral catalog. Pass fields=*variants.calculated_price to include prices, otherwise variants have no price attached.",
        "tags": [
          "Catalog"
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "query",
            "required": false,
            "description": "Filter to a single product by URL slug, e.g. \"tongue-scraper\".",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max products to return (default 12).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Pagination offset.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "region_id",
            "in": "query",
            "required": false,
            "description": "Region to price against. Get one from GET /api/store/regions. Defaults to the store's default region (EUR).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "description": "Comma-separated field selection. Use \"*variants.calculated_price\" to include prices and \"+variants.inventory_quantity\" to include stock.",
            "schema": {
              "type": "string"
            },
            "example": "*variants.calculated_price,+variants.inventory_quantity"
          }
        ],
        "responses": {
          "200": {
            "description": "The catalog page.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "A Zoral product.",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "handle": {
                            "type": "string",
                            "description": "URL slug, e.g. \"tongue-scraper\". Stable; prefer it over id for lookups."
                          },
                          "description": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "HTML."
                          },
                          "material": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "thumbnail": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "variants": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "description": "A purchasable variant of a product.",
                              "properties": {
                                "id": {
                                  "type": "string",
                                  "description": "Variant id. This is what a cart line item references."
                                },
                                "title": {
                                  "type": "string"
                                },
                                "sku": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "manage_inventory": {
                                  "type": "boolean"
                                },
                                "allow_backorder": {
                                  "type": "boolean"
                                },
                                "inventory_quantity": {
                                  "type": [
                                    "integer",
                                    "null"
                                  ],
                                  "description": "Only present when requested via the fields parameter."
                                },
                                "calculated_price": {
                                  "type": "object",
                                  "properties": {
                                    "calculated_amount": {
                                      "type": "number",
                                      "description": "Price in major units (e.g. 9.99), already tax/region adjusted."
                                    },
                                    "currency_code": {
                                      "type": "string",
                                      "description": "ISO 4217 lowercase, e.g. \"eur\"."
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer",
                      "description": "Total products matching the filter."
                    },
                    "offset": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "Catalog unreachable (transient).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All errors share this shape. Never HTML.",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable code, e.g. \"unknown_endpoint\"."
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable summary."
                        },
                        "resolution": {
                          "type": "string",
                          "description": "What to do next."
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/store/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get a product by id",
        "description": "Returns a single product. Prefer listProducts with the handle filter if you only know the slug.",
        "tags": [
          "Catalog"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Product id, e.g. prod_01J...",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "region_id",
            "in": "query",
            "required": false,
            "description": "Region to price against. Get one from GET /api/store/regions. Defaults to the store's default region (EUR).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fields",
            "in": "query",
            "required": false,
            "description": "Comma-separated field selection. Use \"*variants.calculated_price\" to include prices and \"+variants.inventory_quantity\" to include stock.",
            "schema": {
              "type": "string"
            },
            "example": "*variants.calculated_price,+variants.inventory_quantity"
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "type": "object",
                      "description": "A Zoral product.",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "title": {
                          "type": "string"
                        },
                        "handle": {
                          "type": "string",
                          "description": "URL slug, e.g. \"tongue-scraper\". Stable; prefer it over id for lookups."
                        },
                        "description": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "HTML."
                        },
                        "material": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "thumbnail": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "variants": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "description": "A purchasable variant of a product.",
                            "properties": {
                              "id": {
                                "type": "string",
                                "description": "Variant id. This is what a cart line item references."
                              },
                              "title": {
                                "type": "string"
                              },
                              "sku": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "manage_inventory": {
                                "type": "boolean"
                              },
                              "allow_backorder": {
                                "type": "boolean"
                              },
                              "inventory_quantity": {
                                "type": [
                                  "integer",
                                  "null"
                                ],
                                "description": "Only present when requested via the fields parameter."
                              },
                              "calculated_price": {
                                "type": "object",
                                "properties": {
                                  "calculated_amount": {
                                    "type": "number",
                                    "description": "Price in major units (e.g. 9.99), already tax/region adjusted."
                                  },
                                  "currency_code": {
                                    "type": "string",
                                    "description": "ISO 4217 lowercase, e.g. \"eur\"."
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "All errors share this shape. Never HTML.",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Stable machine-readable code, e.g. \"unknown_endpoint\"."
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable summary."
                        },
                        "resolution": {
                          "type": "string",
                          "description": "What to do next."
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/store/reviews": {
      "get": {
        "operationId": "listReviews",
        "summary": "List customer reviews",
        "description": "Real, verified customer reviews plus the aggregate rating. Omit product_id for reviews across the whole store.",
        "tags": [
          "Reviews"
        ],
        "parameters": [
          {
            "name": "product_id",
            "in": "query",
            "required": false,
            "description": "Restrict to one product.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reviews and aggregate rating.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reviews": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "author": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "quote": {
                            "type": "string"
                          },
                          "rating": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 5
                          },
                          "review_date": {
                            "type": "string",
                            "format": "date"
                          },
                          "verified": {
                            "type": "boolean",
                            "description": "True when tied to a real purchase."
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "avg_rating": {
                      "type": "number",
                      "description": "Mean rating, 1-5. 0 when there are no reviews."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/store/regions": {
      "get": {
        "operationId": "listRegions",
        "summary": "List regions",
        "description": "Regions determine currency and which countries are served. Use a region id with listProducts to get correct prices.",
        "tags": [
          "Regions"
        ],
        "responses": {
          "200": {
            "description": "Available regions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "regions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "currency_code": {
                            "type": "string"
                          },
                          "countries": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "iso_2": {
                                  "type": "string"
                                },
                                "display_name": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/store/collections": {
      "get": {
        "operationId": "listCollections",
        "summary": "List collections",
        "description": "Product collections, e.g. the Featured collection shown on the homepage.",
        "tags": [
          "Catalog"
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "query",
            "required": false,
            "description": "Filter by slug, e.g. \"frontpage\".",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collections.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "collections": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "handle": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Product": {
        "type": "object",
        "description": "A Zoral product.",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "handle": {
            "type": "string",
            "description": "URL slug, e.g. \"tongue-scraper\". Stable; prefer it over id for lookups."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "HTML."
          },
          "material": {
            "type": [
              "string",
              "null"
            ]
          },
          "thumbnail": {
            "type": [
              "string",
              "null"
            ]
          },
          "variants": {
            "type": "array",
            "items": {
              "type": "object",
              "description": "A purchasable variant of a product.",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Variant id. This is what a cart line item references."
                },
                "title": {
                  "type": "string"
                },
                "sku": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "manage_inventory": {
                  "type": "boolean"
                },
                "allow_backorder": {
                  "type": "boolean"
                },
                "inventory_quantity": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Only present when requested via the fields parameter."
                },
                "calculated_price": {
                  "type": "object",
                  "properties": {
                    "calculated_amount": {
                      "type": "number",
                      "description": "Price in major units (e.g. 9.99), already tax/region adjusted."
                    },
                    "currency_code": {
                      "type": "string",
                      "description": "ISO 4217 lowercase, e.g. \"eur\"."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ProductVariant": {
        "type": "object",
        "description": "A purchasable variant of a product.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Variant id. This is what a cart line item references."
          },
          "title": {
            "type": "string"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "manage_inventory": {
            "type": "boolean"
          },
          "allow_backorder": {
            "type": "boolean"
          },
          "inventory_quantity": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Only present when requested via the fields parameter."
          },
          "calculated_price": {
            "type": "object",
            "properties": {
              "calculated_amount": {
                "type": "number",
                "description": "Price in major units (e.g. 9.99), already tax/region adjusted."
              },
              "currency_code": {
                "type": "string",
                "description": "ISO 4217 lowercase, e.g. \"eur\"."
              }
            }
          }
        }
      },
      "Money": {
        "type": "object",
        "properties": {
          "calculated_amount": {
            "type": "number",
            "description": "Price in major units (e.g. 9.99), already tax/region adjusted."
          },
          "currency_code": {
            "type": "string",
            "description": "ISO 4217 lowercase, e.g. \"eur\"."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "All errors share this shape. Never HTML.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code, e.g. \"unknown_endpoint\"."
              },
              "message": {
                "type": "string",
                "description": "Human-readable summary."
              },
              "resolution": {
                "type": "string",
                "description": "What to do next."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      }
    }
  }
}