{
  "openapi": "3.0.0",
  "info": {
    "title": "SitePack App API",
    "description": "The REST API installed apps use to read and change a SitePack site: content,\ncommerce, domains and mailboxes.\n\n**Machine-readable:** the full [OpenAPI document](https://sitepack.dev/openapi.json), one [Markdown file per section](https://sitepack.dev/llms.txt) (for example [Leads](https://sitepack.dev/api-reference/leads.md)) and an index in [llms.txt](https://sitepack.dev/llms.txt).\n\n## Authorization\n\nThe app API uses the same OAuth flow as the SitePack CLI.\n\n```http\nGET /api/authentication/oauth/authorize\n    ?client_id=your-client-id\n    &scope=content:write%20media:read\n    &redirect_uri=https://your-app.example/callback\n    &state=csrf-token\n```\n\nThe merchant picks a site and approves the scopes you asked for, then you are sent\nback with a `code`. Exchange it:\n\n```http\nPOST /api/authentication/oauth/token\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=authorization_code&code=…&client_id=…&client_secret=…\n```\n\nSend the result on every request. `X-SitePack-Access-Token` is the preferred header;\na standard `Authorization: Bearer <access_token>` is also accepted:\n\n```http\nX-SitePack-Access-Token: <access_token>\n```\n\nAccess tokens last an hour; refresh them with `grant_type=refresh_token`.\n\n**A token is bound to one site.** The site is never a parameter — an app that\nserves ten merchants holds ten tokens. That is also why a uuid from one site is\nsimply *not found* on another.\n\n## What your token can do\n\nThree things must hold, and they are checked on **every** request:\n\n1. your installation on that site is active;\n2. the app's subscription on that site is active — if the merchant cancels a paid\n   app, API access stops immediately;\n3. the scope is one the merchant actually granted.\n\nPoint three is an intersection, re-evaluated per request: if a merchant removes a\nscope, it stops working at once rather than when your hour-long token expires.\n`GET /me` shows the scopes in force right now.\n\n**An app with no scopes can reach `/me` and `/scopes`, and nothing else.**\n\n## Scopes\n\nScopes read `section:action`. **`write` implies `read`**; nothing else is implied,\nand there is no wildcard scope.\n\nTwo scopes spend the merchant's money and therefore stand alone — `domains:order`\nand `mail:order`. Holding `domains:write` does *not* let you order a domain.\n\nAsk for the narrowest set that does the job: merchants see every scope you request\non the consent screen.\n\n## Identifiers\n\nEverything is addressed by **uuid**. No endpoint takes an auto-increment id and no\nresponse returns one. New records get a uuid v7, which is time-ordered, so sorting\nby uuid sorts by creation.\n\n## Responses\n\nEvery response carries a `status`. Lists are paginated with `page` and `limit`\n(max 100):\n\n```json\n{\n  \"status\": \"success\",\n  \"items\": [],\n  \"pagination\": { \"page\": 1, \"limit\": 25, \"totalRecords\": 42, \"totalPages\": 2 }\n}\n```\n\nErrors name a machine-readable code — switch on `error.code`, never on the message:\n\n```json\n{\n  \"status\": \"error\",\n  \"error\": { \"code\": \"scope_required\", \"message\": \"…\", \"scope\": \"content:write\" }\n}\n```\n\n| Status | Meaning |\n| --- | --- |\n| `400` | Malformed request |\n| `401` | No token, or it is invalid or expired |\n| `403` | Missing scope, or the installation is no longer active |\n| `404` | Not found — or on another site, which reads the same on purpose |\n| `409` | Conflict: slug taken, no free mailbox slot, unpaid invoices |\n| `422` | Validation failed; `error.field` names the field |\n| `429` | Rate limited; `Retry-After` says for how long |\n| `500` | Failed on our side. `internal_error`, with a deliberately generic message — the detail is in our logs, not in your response |\n\n## Rate limits\n\nEvery response carries your budget, so you never have to discover a limit by being\ncut off:\n\n```http\nX-SitePack-RateLimit: 600\nX-SitePack-RateLimit-Tokens: 574\nX-SitePack-RateLimit-Reset: 1750000060\n```\n\n| Bucket | Limit | Keyed on |\n| --- | --- | --- |\n| Everything | 600 / minute | your installation — app *and* site |\n| Endpoints behind an `:order` scope | 20 / hour | your installation |\n| Refused access tokens | 60 / minute | calling IP address |\n| `/docs` and `/docs.json` | 60 / minute | calling IP address |\n| The OAuth endpoints | 120 / minute | calling IP address |\n\nKeying the main budget on the installation rather than on your address means serving\nmany merchants from one server does not throttle you — but it also means one app of\nyours cannot spend another's budget.\n\nA `429` names `Retry-After` in seconds. Wait that long; retrying sooner only spends\nwhat is left of the window.\n\nThe order bucket is separate because those endpoints charge the merchant: a loop that\nlists domains is a mistake, a loop that *buys* them is an invoice.\n\n## Webhooks\n\nSubscriptions are declared in your `app.json` manifest. Payloads are deliberately\nthin: `arguments` carries **only the uuid** of the resource that changed. Call the\nAPI with that uuid to read the details.\n\nThat means a payload can never contain something you are not scoped to read, and\ncan never be stale — by the time a retry lands, embedded values might describe a\nversion that no longer exists, while a uuid always resolves to the current one.\n\nTreat an event as \"go look at this\", not as data.\n\n## Things worth knowing before you start\n\n- **Pages are styled through element settings, not inline HTML.** Inline `style=`\n  attributes are stripped from anything you submit; alignment and colour are lifted\n  into the element's `settings` so the intent survives.\n- **Elements cannot carry script.** `<script>` blocks, `on…=` handlers and\n  `javascript:` URLs are removed from element values. Markup, links and embeds are\n  left alone — it is scripting specifically that is not yours to add, because the\n  merchant opens your page in their own admin session.\n- **Responses are never cached.** They come back `Cache-Control: no-store, private`,\n  since most of what they contain is the merchant's data and all of it is specific to\n  your token.\n- **Order creation prices from the catalogue.** A price in your payload is ignored;\n  orders are created unpaid at status `new`.\n- **Stock moves by delta**, not by absolute value, so two apps adjusting at once do\n  not overwrite each other.\n- **Paid orders depend on the merchant.** On a site with €1+ monthly revenue the\n  mandate is already active and the order is charged (`\"charged\": true`); below\n  that you get a `checkout_url` for the merchant to confirm. Handle both.\n\n## Where to read the other half\n\nThis reference says what an endpoint does. The help centre says what the thing\nit changes means to the merchant looking at it — worth reading before you\nchange it on their behalf.\n\n| What you are working on | Help centre |\n| --- | --- |\n| Building and publishing an app | [Developing apps](https://help.sitepack.eu/docs/partners/apps) |\n| Pages, blogs, media and categories | [Managing content](https://help.sitepack.eu/docs/admin/content) |\n| Menus and navigation | [Menus and navigation](https://help.sitepack.eu/docs/admin/content/navigation) |\n| Translating content | [Translating content](https://help.sitepack.eu/docs/admin/content/translations) |\n| Themes, layouts and element areas | [Design and themes](https://help.sitepack.eu/docs/admin/design) |\n| Products, collections and stock | [Products](https://help.sitepack.eu/docs/admin/store/products) |\n| Orders and their statuses | [Orders](https://help.sitepack.eu/docs/admin/store/orders) |\n| Form submissions | [Form submissions](https://help.sitepack.eu/docs/admin/leads) |\n| Domains and DNS | [Domains and DNS](https://help.sitepack.eu/docs/domains/overview) |\n| Mailboxes | [Mail services](https://help.sitepack.eu/docs/mail/overview) |\n| Subscription, plan and invoices | [Subscription and plan](https://help.sitepack.eu/docs/admin/settings/subscription) |\n",
    "version": "v1"
  },
  "paths": {
    "/api/public/v1/content/validate-slug": {
      "get": {
        "tags": [
          "Content"
        ],
        "summary": "Check a slug is free",
        "description": "**Required scope:** `content:read` — Read pages, their elements and revisions",
        "operationId": "contentValidateSlug",
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "description": "The slug to test.",
            "required": true,
            "schema": {
              "description": "The slug to test.",
              "type": "string",
              "example": "about-us"
            }
          },
          {
            "name": "parent_uuid",
            "in": "query",
            "description": "Test the slug under this parent page. Slugs are unique per parent, not per site.",
            "required": false,
            "schema": {
              "description": "Test the slug under this parent page. Slugs are unique per parent, not per site.",
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Whether a slug is still free on this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "slug": "about-us",
                  "valid": false
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:read\" scope, which this app was not granted.",
                    "scope": "content:read"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"slug\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"slug\" is required.",
                    "field": "slug"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/check": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Check domain availability",
        "description": "**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "operationId": "domainsCheck",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "description": "The name to check. Without an extension every extension we sell is checked.",
            "required": true,
            "schema": {
              "description": "The name to check. Without an extension every extension we sell is checked.",
              "type": "string",
              "example": "wheelhouse"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Availability and price for a domain name",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "results": [
                    {
                      "domain": "wheelhouse.nl",
                      "extension": "nl",
                      "available": false,
                      "price_cents": 995
                    },
                    {
                      "domain": "wheelhouse.be",
                      "extension": "be",
                      "available": true,
                      "price_cents": 1295
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"domain\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"domain\" is required.",
                    "field": "domain"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/suggest": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Suggest domain names",
        "description": "**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "operationId": "domainsSuggest",
        "responses": {
          "200": {
            "description": "Suggested available domains for this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "suggestions": [
                    {
                      "domain": "wheelhouse.shop",
                      "extension": "shop",
                      "available": true,
                      "price_cents": 2495
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/order": {
      "post": {
        "tags": [
          "Domains"
        ],
        "summary": "Order a domain",
        "description": "Order one or more domains.\n\nOn a site with an MRR of at least one euro the mandate is already active, so the\norder is charged straight away. On a free or trial site a checkout URL comes back\ninstead and the merchant confirms - an app cannot make someone who has never paid\nstart paying.\n\n**Required scope:** `domains:order` — Order domains, which may be charged to the merchant",
        "externalDocs": {
          "description": "Domains on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/domains"
        },
        "operationId": "domainsOrder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "domains"
                ],
                "properties": {
                  "domains": {
                    "description": "The domain names to order, with their extension. A single string is accepted too.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "wheelhouse.be"
                    ]
                  },
                  "holder_uuid": {
                    "description": "The registrant contact to register under, from `GET /domains/holders`. The site's default holder is used when it is left out.",
                    "type": "string",
                    "example": "0192c5c6-8a34-7b19-9d02-46e5f7013b8a"
                  }
                },
                "type": "object"
              },
              "example": {
                "domains": [
                  "wheelhouse.be"
                ],
                "holder_uuid": "0192c5c6-8a34-7b19-9d02-46e5f7013b8a"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The order, either charged or awaiting checkout",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "order_uuid": "0192c5e8-4c19-7a53-b806-7d2f5a91e0c4",
                  "added": [
                    "wheelhouse.be"
                  ],
                  "skipped": [],
                  "charged": false,
                  "checkout_url": "/admin/shop/checkout/0192c5e8-4c19-7a53-b806-7d2f5a91e0c4"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:order\" scope, which this app was not granted.",
                    "scope": "domains:order"
                  }
                }
              }
            }
          },
          "409": {
            "description": "None of the requested domains could be ordered. They may be taken, invalid or already in the cart.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "domains_unavailable",
                    "message": "None of the requested domains could be ordered. They may be taken, invalid or already in the cart."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"domains\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"domains\" is required.",
                    "field": "domains"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:order"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/holders": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List domain holders",
        "description": "The registrant contacts available on this site.\n\n**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "operationId": "domainsHolders",
        "responses": {
          "200": {
            "description": "The domain holders of this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "holders": [
                    {
                      "uuid": "0192c5c6-8a34-7b19-9d02-46e5f7013b8a",
                      "company_name": "Wheelhouse Ltd",
                      "first_name": "Emma",
                      "last_name": "Clarke",
                      "city": "London",
                      "country_code": "GB"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/elements": {
      "get": {
        "tags": [
          "Elements"
        ],
        "summary": "List element areas",
        "description": "List the areas an app may address.\n\n**Required scope:** `elements:read` — Read homepage, footer and sidebar elements",
        "operationId": "elementsAreas",
        "responses": {
          "200": {
            "description": "The addressable element areas",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "areas": [
                    "homepage",
                    "footer",
                    "sidebar"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"elements:read\" scope, which this app was not granted.",
                    "scope": "elements:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "elements:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/mail/slots": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "Read free mailbox slots",
        "description": "**Required scope:** `mail:read` — Read mailboxes and mailbox slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailSlots",
        "responses": {
          "200": {
            "description": "Used, total and free mailbox slots",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "used": 2,
                  "total": 3,
                  "free": 1
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:read\" scope, which this app was not granted.",
                    "scope": "mail:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/theme/layouts": {
      "get": {
        "tags": [
          "Theme"
        ],
        "summary": "List theme layouts",
        "description": "List the layouts a page can be assigned to.\n\n**Required scope:** `themes:read` — Read the active theme, its layouts and its design settings",
        "operationId": "themeLayouts",
        "responses": {
          "200": {
            "description": "The layouts of the active theme",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "layouts": [
                    {
                      "name": "Standaard",
                      "template": "layouts/default.html.twig"
                    },
                    {
                      "name": "Landingspagina",
                      "template": "layouts/landing.html.twig"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"themes:read\" scope, which this app was not granted.",
                    "scope": "themes:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "themes:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/theme/settings": {
      "get": {
        "tags": [
          "Theme"
        ],
        "summary": "Read theme design settings",
        "description": "Read the theme's design settings: its declared colours/fonts/etc. and the values\nthe staging preview currently renders with.\n\n**Required scope:** `themes:read` — Read the active theme, its layouts and its design settings",
        "operationId": "themeSettings",
        "responses": {
          "200": {
            "description": "The theme design settings and current staging values",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "theme": {
                    "uuid": "019200a1-7b3c-7c1a-9b2e-2f4d6a8c0e11",
                    "name": "Modern",
                    "watching": true
                  },
                  "settings": [
                    {
                      "key": "header-background-color",
                      "type": "colour",
                      "label": "Header background",
                      "default": "#ffffff"
                    },
                    {
                      "key": "heading-font",
                      "type": "font",
                      "label": "Heading font",
                      "default": "Open_Sans"
                    },
                    {
                      "key": "link-text-decoration",
                      "type": "options",
                      "label": "Link underline style",
                      "default": "underline",
                      "options": [
                        "none",
                        "underline",
                        "overline",
                        "line-through"
                      ]
                    }
                  ],
                  "values": {
                    "header-background-color": "#0b1220",
                    "heading-font": "Roboto"
                  },
                  "staging_overrides": {
                    "header-background-color": "#0b1220",
                    "heading-font": "Roboto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"themes:read\" scope, which this app was not granted.",
                    "scope": "themes:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "themes:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Theme"
        ],
        "summary": "Change theme design settings",
        "description": "Change the theme design settings on the staging preview. Only the keys sent are\nchanged; the values apply to the staging site only, never the live site, until a\nperson promotes them in the admin.\n\n**Required scope:** `themes:write` — Change the theme design settings (colours, fonts) on the staging preview",
        "operationId": "themeSettingsUpdate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "settings"
                ],
                "properties": {
                  "settings": {
                    "description": "The setting values to change, keyed by setting key. Only the keys you send are changed. They apply to the staging preview only, never the live site, until a person promotes them in the admin. Values are staging-scoped by design.",
                    "type": "object",
                    "example": {
                      "header-background-color": "#0b1220",
                      "heading-font": "Roboto"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "settings": {
                  "header-background-color": "#0b1220",
                  "heading-font": "Roboto"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated staging design settings",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "theme": {
                    "uuid": "019200a1-7b3c-7c1a-9b2e-2f4d6a8c0e11",
                    "name": "Modern",
                    "watching": true
                  },
                  "settings": [
                    {
                      "key": "header-background-color",
                      "type": "colour",
                      "label": "Header background",
                      "default": "#ffffff"
                    },
                    {
                      "key": "heading-font",
                      "type": "font",
                      "label": "Heading font",
                      "default": "Open_Sans"
                    },
                    {
                      "key": "link-text-decoration",
                      "type": "options",
                      "label": "Link underline style",
                      "default": "underline",
                      "options": [
                        "none",
                        "underline",
                        "overline",
                        "line-through"
                      ]
                    }
                  ],
                  "values": {
                    "header-background-color": "#0b1220",
                    "heading-font": "Roboto"
                  },
                  "staging_overrides": {
                    "header-background-color": "#0b1220",
                    "heading-font": "Roboto"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"themes:write\" scope, which this app was not granted.",
                    "scope": "themes:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"settings\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"settings\" is required.",
                    "field": "settings"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "themes:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/translations/locales": {
      "get": {
        "tags": [
          "Translations"
        ],
        "summary": "List site locales",
        "description": "**Required scope:** `translations:read` — Read content translations and site locales",
        "externalDocs": {
          "description": "Site locales",
          "url": "https://help.sitepack.eu/docs/admin/settings/translations"
        },
        "operationId": "translationsLocales",
        "responses": {
          "200": {
            "description": "The locales that are active on this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "locales": [
                    "en",
                    "de",
                    "nl"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:read\" scope, which this app was not granted.",
                    "scope": "translations:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/translations/review/{locale}": {
      "get": {
        "tags": [
          "Translations"
        ],
        "summary": "List what needs translating",
        "description": "What still needs doing in one locale: items with no translation at all, and\nitems whose translation has gone stale because the source text changed.\n\n**Required scope:** `translations:read` — Read content translations and site locales",
        "operationId": "translationsReview",
        "parameters": [
          {
            "name": "locale",
            "in": "path",
            "description": "A locale that is active on this site. `GET /translations/locales` lists them.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "de"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only this content type. All four are walked when it is left out.",
            "required": false,
            "schema": {
              "description": "Only this content type. All four are walked when it is left out.",
              "type": "string",
              "enum": [
                "page",
                "blog",
                "product",
                "category"
              ],
              "example": "page"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to report per bucket. Clamped to 1-500, defaults to 100.",
            "required": false,
            "schema": {
              "description": "How many items to report per bucket. Clamped to 1-500, defaults to 100.",
              "type": "integer",
              "example": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Missing and outdated translations for one locale",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "locale": "de",
                  "missing": [
                    {
                      "type": "page",
                      "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                      "name": "About us",
                      "strings": 14,
                      "translated": 0,
                      "outdated": 0
                    }
                  ],
                  "needs_review": [
                    {
                      "type": "blog",
                      "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                      "name": "Winter tyres: what to look for",
                      "strings": 22,
                      "translated": 22,
                      "outdated": 3
                    }
                  ],
                  "totals": {
                    "total": 412,
                    "translated": 355,
                    "outdated": 3,
                    "missing_items": 1,
                    "needs_review_items": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:read\" scope, which this app was not granted.",
                    "scope": "translations:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/translations/coverage": {
      "get": {
        "tags": [
          "Translations"
        ],
        "summary": "Read translation coverage",
        "description": "How far along every active locale is - enough to decide which one to work on\nnext without walking each item.\n\n**Required scope:** `translations:read` — Read content translations and site locales",
        "operationId": "translationsCoverage",
        "responses": {
          "200": {
            "description": "Translation coverage per active locale",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "locales": [
                    {
                      "locale": "de",
                      "strings": 412,
                      "translated": 355,
                      "outdated": 3,
                      "missing_items": 1,
                      "needs_review_items": 1,
                      "percentage_translated": 86
                    },
                    {
                      "locale": "nl",
                      "strings": 412,
                      "translated": 412,
                      "outdated": 0,
                      "missing_items": 0,
                      "needs_review_items": 0,
                      "percentage_translated": 100
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:read\" scope, which this app was not granted.",
                    "scope": "translations:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/app/settings": {
      "get": {
        "tags": [
          "App settings"
        ],
        "summary": "Read app settings",
        "description": "**Required scope:** `app:settings:read` — Read this app's own settings on the site",
        "operationId": "appSettingsGet",
        "responses": {
          "200": {
            "description": "This app's settings on this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "settings": {
                    "sync_interval": "900",
                    "warehouse": "utrecht"
                  },
                  "merchant_settings": {
                    "container_id": "GTM-ABC1234",
                    "consent_mode": true,
                    "track_purchase": true
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"app:settings:read\" scope, which this app was not granted.",
                    "scope": "app:settings:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "app:settings:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "App settings"
        ],
        "summary": "Replace app settings",
        "description": "Replace the settings. A key left out of the payload is removed.\n\n**Required scope:** `app:settings:write` — Change this app's own settings on the site",
        "operationId": "appSettingsReplace",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "settings"
                ],
                "properties": {
                  "settings": {
                    "description": "The complete settings map for this app on this site. A replace, not a merge: a key left out is removed. Values are stored as strings; arrays and objects are JSON-encoded first.",
                    "type": "object",
                    "example": {
                      "sync_interval": "900",
                      "warehouse": "utrecht"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "settings": {
                  "sync_interval": "900",
                  "warehouse": "utrecht"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored settings",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "settings": {
                    "sync_interval": "900",
                    "warehouse": "utrecht"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"app:settings:write\" scope, which this app was not granted.",
                    "scope": "app:settings:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"settings\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"settings\" is required.",
                    "field": "settings"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "app:settings:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/blogs": {
      "get": {
        "tags": [
          "Blogs"
        ],
        "summary": "List blog posts",
        "description": "**Required scope:** `blogs:read` — Read blog posts and blog categories",
        "operationId": "blogsList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the post title and slug.",
            "required": false,
            "schema": {
              "description": "Free-text search over the post title and slug.",
              "type": "string",
              "example": "winter"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of blog posts",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                      "title": "Winter tyres: what to look for",
                      "slug": "winter-tyres-what-to-look-for",
                      "status": "published",
                      "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                      "tags": [
                        "maintenance",
                        "winter"
                      ],
                      "created": "2026-02-11T08:00:00+00:00",
                      "modified": "2026-02-13T16:44:02+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 18,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:read\" scope, which this app was not granted.",
                    "scope": "blogs:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Blogs"
        ],
        "summary": "Create a blog post",
        "description": "**Required scope:** `blogs:write` — Create, change, publish and delete blog posts",
        "operationId": "blogsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "description": "The post title.",
                    "type": "string",
                    "example": "Winter tyres: what to look for"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out.",
                    "type": "string",
                    "example": "winter-tyres-what-to-look-for"
                  },
                  "status": {
                    "description": "Defaults to `draft`. Publishing also indexes the post for site search.",
                    "type": "string",
                    "enum": [
                      "published",
                      "draft"
                    ],
                    "example": "draft"
                  },
                  "tags": {
                    "description": "Tag names. A comma-separated string is accepted too. Replaces the current tags.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "maintenance",
                      "winter"
                    ]
                  },
                  "main_image": {
                    "description": "The header image URL, also used as the social preview.",
                    "type": "string",
                    "example": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "custom_title": "Winter tyres for your bike | Wheelhouse",
                      "meta_description": "What to look for when choosing winter tyres."
                    }
                  },
                  "elements": {
                    "description": "The element tree of the post, sanitised the same way a page's is.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Winter tyres: what to look for",
                "slug": "winter-tyres-what-to-look-for",
                "status": "draft",
                "tags": [
                  "maintenance",
                  "winter"
                ],
                "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                "seo": {
                  "custom_title": "Winter tyres for your bike | Wheelhouse",
                  "meta_description": "What to look for when choosing winter tyres."
                },
                "elements": [
                  {
                    "type": "pageHeading",
                    "value": "About us",
                    "settings": {
                      "align": "center"
                    }
                  },
                  {
                    "type": "text",
                    "value": "<p>We have been building bikes since 1978.</p>",
                    "settings": {
                      "align": "left",
                      "textColor": "#202223"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created blog post",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "blog": {
                    "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                    "title": "Winter tyres: what to look for",
                    "slug": "winter-tyres-what-to-look-for",
                    "status": "published",
                    "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                    "tags": [
                      "maintenance",
                      "winter"
                    ],
                    "created": "2026-02-11T08:00:00+00:00",
                    "modified": "2026-02-13T16:44:02+00:00",
                    "seo": {
                      "custom_title": "Winter tyres for your bike | Wheelhouse",
                      "meta_description": "What to look for when choosing winter tyres.",
                      "focus_keyword": "winter bike tyres"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:write\" scope, which this app was not granted.",
                    "scope": "blogs:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" is required.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/categories": {
      "get": {
        "tags": [
          "Categories"
        ],
        "summary": "List categories",
        "description": "**Required scope:** `categories:read` — Read content and product categories",
        "operationId": "categoriesList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the category name and slug.",
            "required": false,
            "schema": {
              "description": "Free-text search over the category name and slug.",
              "type": "string",
              "example": "race"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only categories of this kind.",
            "required": false,
            "schema": {
              "description": "Only categories of this kind.",
              "type": "string",
              "enum": [
                "blog",
                "page",
                "product"
              ],
              "example": "product"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of categories",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                      "name": "Racefietsen",
                      "slug": "road-bikes",
                      "type": "product",
                      "seo": {
                        "custom_title": "Buy road bikes | Wheelhouse",
                        "meta_description": "Our full range of road bikes.",
                        "focus_keyword": "buy road bike"
                      },
                      "created": "2025-11-03T15:22:09+00:00",
                      "modified": "2026-04-18T09:07:44+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 12,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"categories:read\" scope, which this app was not granted.",
                    "scope": "categories:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "categories:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Categories"
        ],
        "summary": "Create a category",
        "description": "**Required scope:** `categories:write` — Create, change and delete categories",
        "operationId": "categoriesCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "description": "The category name.",
                    "type": "string",
                    "example": "Racefietsen"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out.",
                    "type": "string",
                    "example": "road-bikes"
                  },
                  "type": {
                    "description": "What the category groups. Defaults to `blog`.",
                    "type": "string",
                    "enum": [
                      "blog",
                      "page",
                      "product"
                    ],
                    "example": "product"
                  },
                  "parent_uuid": {
                    "description": "Nest under another category. Send `null` to move it to the top level; a category cannot be its own parent.",
                    "type": "string",
                    "example": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "custom_title": "Buy road bikes | Wheelhouse",
                      "meta_description": "Our full range of road bikes."
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefietsen",
                "slug": "road-bikes",
                "type": "product",
                "parent_uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                "seo": {
                  "custom_title": "Buy road bikes | Wheelhouse",
                  "meta_description": "Our full range of road bikes."
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created category",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "category": {
                    "uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "type": "product",
                    "seo": {
                      "custom_title": "Buy road bikes | Wheelhouse",
                      "meta_description": "Our full range of road bikes.",
                      "focus_keyword": "buy road bike"
                    },
                    "created": "2025-11-03T15:22:09+00:00",
                    "modified": "2026-04-18T09:07:44+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"categories:write\" scope, which this app was not granted.",
                    "scope": "categories:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" is required.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "categories:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/collections": {
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "List collections",
        "description": "List collections.\n\n**Required scope:** `collections:read` — Read collections and their rules",
        "operationId": "collectionsList",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of collections",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c51b-4e08-7a92-b13c-6d5f80e24a17",
                      "name": "Racefietsen",
                      "slug": "road-bikes",
                      "created": "2026-02-01T10:00:00+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 9,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:read\" scope, which this app was not granted.",
                    "scope": "collections:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Collections"
        ],
        "summary": "Create a collection",
        "description": "Create a collection, optionally with its rules.\n\n**Required scope:** `collections:write` — Create, change and delete collections",
        "operationId": "collectionsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "description": "The collection name.",
                    "type": "string",
                    "example": "Racefietsen"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out.",
                    "type": "string",
                    "example": "road-bikes"
                  },
                  "rules": {
                    "description": "The rules that decide membership. Same shape as `PUT /collections/{uuid}/rules`, applied as part of creating the collection.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "column": "brand",
                        "value": "Wheelhouse",
                        "exact": true
                      },
                      {
                        "column": "name",
                        "value": "Aero",
                        "exact": false
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefietsen",
                "slug": "road-bikes",
                "rules": [
                  {
                    "column": "brand",
                    "value": "Wheelhouse",
                    "exact": true
                  },
                  {
                    "column": "name",
                    "value": "Aero",
                    "exact": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created collection",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "collection": {
                    "uuid": "0192c51b-4e08-7a92-b13c-6d5f80e24a17",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "created": "2026-02-01T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:write\" scope, which this app was not granted.",
                    "scope": "collections:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" is required.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content": {
      "get": {
        "tags": [
          "Content"
        ],
        "summary": "List pages",
        "description": "**Required scope:** `content:read` — Read pages, their elements and revisions",
        "operationId": "contentList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the page title and slug.",
            "required": false,
            "schema": {
              "description": "Free-text search over the page title and slug.",
              "type": "string",
              "example": "about"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only pages of this type.",
            "required": false,
            "schema": {
              "description": "Only pages of this type.",
              "type": "string",
              "enum": [
                "page",
                "landing_page",
                "content"
              ],
              "example": "page"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only pages in this state.",
            "required": false,
            "schema": {
              "description": "Only pages in this state.",
              "type": "string",
              "enum": [
                "published",
                "draft"
              ],
              "example": "published"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of pages",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                      "title": "About us",
                      "slug": "about-us",
                      "type": "page",
                      "status": "published",
                      "locale": "en",
                      "is_menu_item": true,
                      "parent_uuid": null,
                      "created": "2026-03-04T09:12:41+00:00",
                      "modified": "2026-05-19T14:02:07+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 42,
                    "totalPages": 2
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:read\" scope, which this app was not granted.",
                    "scope": "content:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Content"
        ],
        "summary": "Create a page",
        "description": "**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "description": "The page title.",
                    "type": "string",
                    "example": "About us"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out, and unique per parent page.",
                    "type": "string",
                    "example": "about-us"
                  },
                  "type": {
                    "description": "What kind of page this is. Defaults to `page`.",
                    "type": "string",
                    "enum": [
                      "page",
                      "landing_page",
                      "content"
                    ],
                    "example": "page"
                  },
                  "status": {
                    "description": "Defaults to `draft`, so a page an app is still filling in is not live yet.",
                    "type": "string",
                    "enum": [
                      "published",
                      "draft"
                    ],
                    "example": "draft"
                  },
                  "parent_uuid": {
                    "description": "Nest the page under another page. Send `null` to move it back to the top level.",
                    "type": "string",
                    "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
                  },
                  "is_menu_item": {
                    "description": "Offer the page in the menu picker.",
                    "type": "boolean",
                    "example": true
                  },
                  "template_key": {
                    "description": "A theme template to render with. Setting one makes the page a `landing_page`.",
                    "type": "string",
                    "example": "landing/product-launch"
                  },
                  "image": {
                    "description": "The header image URL. Send `null` to clear it.",
                    "type": "string",
                    "example": "https://cdn.sitepack.io/wheelhouse/about-header.jpg"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description`, `open_graph_description`, `focus_keyword` and `no_index`. Only the keys you send are written.",
                    "type": "object",
                    "example": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "no_index": false
                    }
                  },
                  "elements": {
                    "description": "The element tree. Sanitised on the way in: inline `style=` attributes are stripped and scripting removed.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "About us",
                "slug": "about-us",
                "type": "page",
                "status": "draft",
                "parent_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                "is_menu_item": true,
                "template_key": "landing/product-launch",
                "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                "seo": {
                  "custom_title": "About Wheelhouse | Bikes since 1978",
                  "meta_description": "Who we are and where to find us.",
                  "no_index": false
                },
                "elements": [
                  {
                    "type": "pageHeading",
                    "value": "About us",
                    "settings": {
                      "align": "center"
                    }
                  },
                  {
                    "type": "text",
                    "value": "<p>We have been building bikes since 1978.</p>",
                    "settings": {
                      "align": "left",
                      "textColor": "#202223"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "status": "published",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00",
                    "seo": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "open_graph_description": "Building bikes since 1978.",
                      "focus_keyword": "bike shop london",
                      "no_index": false,
                      "score": 78
                    },
                    "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                    "template_key": null,
                    "editor_version": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The slug \"about-us\" is already in use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "slug_exists",
                    "message": "The slug \"about-us\" is already in use."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" is required.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/customers": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "List customers",
        "description": "**Required scope:** `customers:read` — Read customers and their addresses",
        "operationId": "customersList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over name, e-mail and company.",
            "required": false,
            "schema": {
              "description": "Free-text search over name, e-mail and company.",
              "type": "string",
              "example": "jansen"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of customers",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                      "email": "emma@example.com",
                      "first_name": "Emma",
                      "last_name": "Clarke",
                      "street": "Baker Street",
                      "house_number": "12",
                      "house_number_addition": "A",
                      "postal_code": "NW1 6XE",
                      "city": "London",
                      "country": "GB",
                      "company_name": null,
                      "vat_number": null,
                      "phone_number": "+44 20 7946 0958",
                      "created": "2026-05-02T18:21:07+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 1284,
                    "totalPages": 52
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"customers:read\" scope, which this app was not granted.",
                    "scope": "customers:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "customers:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Create a customer",
        "description": "**Required scope:** `customers:write` — Create and change customers",
        "operationId": "customersCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "description": "The customer's e-mail address. Stored lower-case.",
                    "type": "string",
                    "example": "emma@example.com"
                  },
                  "first_name": {
                    "description": "Given name.",
                    "type": "string",
                    "example": "Emma"
                  },
                  "last_name": {
                    "description": "Family name.",
                    "type": "string",
                    "example": "Clarke"
                  },
                  "street": {
                    "description": "Street name, without the number.",
                    "type": "string",
                    "example": "Baker Street"
                  },
                  "house_number": {
                    "description": "House number.",
                    "type": "string",
                    "example": "12"
                  },
                  "house_number_addition": {
                    "description": "Addition to the house number.",
                    "type": "string",
                    "example": "A"
                  },
                  "postal_code": {
                    "description": "Postal code.",
                    "type": "string",
                    "example": "NW1 6XE"
                  },
                  "city": {
                    "description": "City.",
                    "type": "string",
                    "example": "London"
                  },
                  "country": {
                    "description": "Two-letter country code.",
                    "type": "string",
                    "example": "GB"
                  },
                  "business": {
                    "description": "Mark the customer as a business. Only read when the customer is created.",
                    "type": "boolean",
                    "example": false
                  },
                  "company_name": {
                    "description": "Company name, for business customers.",
                    "type": "string",
                    "example": "Wheelhouse Ltd"
                  },
                  "vat_number": {
                    "description": "VAT number, for business customers.",
                    "type": "string",
                    "example": "NL001234567B01"
                  },
                  "phone_number": {
                    "description": "Phone number.",
                    "type": "string",
                    "example": "+44 20 7946 0958"
                  }
                },
                "type": "object"
              },
              "example": {
                "email": "emma@example.com",
                "first_name": "Emma",
                "last_name": "Clarke",
                "street": "Baker Street",
                "house_number": "12",
                "house_number_addition": "A",
                "postal_code": "NW1 6XE",
                "city": "London",
                "country": "GB",
                "business": false,
                "company_name": "Wheelhouse Ltd",
                "vat_number": "NL001234567B01",
                "phone_number": "+44 20 7946 0958"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created customer",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "customer": {
                    "uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "email": "emma@example.com",
                    "first_name": "Emma",
                    "last_name": "Clarke",
                    "street": "Baker Street",
                    "house_number": "12",
                    "house_number_addition": "A",
                    "postal_code": "NW1 6XE",
                    "city": "London",
                    "country": "GB",
                    "company_name": null,
                    "vat_number": null,
                    "phone_number": "+44 20 7946 0958",
                    "created": "2026-05-02T18:21:07+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"customers:write\" scope, which this app was not granted.",
                    "scope": "customers:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"email\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"email\" is required.",
                    "field": "email"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "customers:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/discount-codes": {
      "get": {
        "tags": [
          "Discounts"
        ],
        "summary": "List discount codes",
        "description": "**Required scope:** `discounts:read` — Read discount codes",
        "operationId": "discountsList",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of discount codes",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c54e-2c85-7a06-b7d3-49f61e830c92",
                      "code": "LENTE10",
                      "type": "percentage",
                      "value": 10,
                      "status": 1,
                      "redeemable": true,
                      "archived": false,
                      "used_at": null,
                      "created": "2026-03-01T08:00:00+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 31,
                    "totalPages": 2
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"discounts:read\" scope, which this app was not granted.",
                    "scope": "discounts:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "discounts:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Discounts"
        ],
        "summary": "Create a discount code",
        "description": "**Required scope:** `discounts:write` — Create, change and delete discount codes",
        "operationId": "discountsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "code",
                  "value"
                ],
                "properties": {
                  "code": {
                    "description": "The code a customer types at checkout. Stored upper-case and unique per site.",
                    "type": "string",
                    "example": "LENTE10"
                  },
                  "type": {
                    "description": "What `value` means. Defaults to `fixed`.",
                    "type": "string",
                    "enum": [
                      "fixed",
                      "percentage"
                    ],
                    "example": "percentage"
                  },
                  "value": {
                    "description": "An amount in cents for `fixed`, or a whole percentage (1-100) for `percentage`.",
                    "type": "integer",
                    "example": 10
                  }
                },
                "type": "object"
              },
              "example": {
                "code": "LENTE10",
                "type": "percentage",
                "value": 10
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created discount code",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "discount_code": {
                    "uuid": "0192c54e-2c85-7a06-b7d3-49f61e830c92",
                    "code": "LENTE10",
                    "type": "percentage",
                    "value": 10,
                    "status": 1,
                    "redeemable": true,
                    "archived": false,
                    "used_at": null,
                    "created": "2026-03-01T08:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"discounts:write\" scope, which this app was not granted.",
                    "scope": "discounts:write"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The discount code \"LENTE10\" already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "discount_code_exists",
                    "message": "The discount code \"LENTE10\" already exists."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"code\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"code\" is required.",
                    "field": "code"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "discounts:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List domains",
        "description": "**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "operationId": "domainsList",
        "responses": {
          "200": {
            "description": "The domains of this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "domains": [
                    {
                      "uuid": "0192c582-8b47-7d31-a05e-6c29f74b1e83",
                      "domain": "wheelhouse.nl",
                      "status": 10,
                      "protocol": "https",
                      "auto_renewal": true,
                      "provider": "openprovider",
                      "dns_provider": "sitepack",
                      "mail_provider": "qboxmail"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/elements/{area}": {
      "get": {
        "tags": [
          "Elements"
        ],
        "summary": "Get an element area",
        "description": "Read one element area.\n\n**Required scope:** `elements:read` — Read homepage, footer and sidebar elements",
        "operationId": "elementsGet",
        "parameters": [
          {
            "name": "area",
            "in": "path",
            "description": "The element area: `homepage`, `footer` or `sidebar`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "footer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The elements of one area, in render order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "area": "footer",
                  "elements": [
                    {
                      "type": "text",
                      "content": "<p>Wheelhouse Ltd, 12 Baker Street, London</p>",
                      "background_color": "#f6f6f7",
                      "text_color": "#202223",
                      "background": null,
                      "sort_order": 1,
                      "row_order": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"elements:read\" scope, which this app was not granted.",
                    "scope": "elements:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That element area does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That element area does not exist on this site.",
                    "resource": "element area"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "elements:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Elements"
        ],
        "summary": "Replace an element area",
        "description": "Replace one element area.\n\nThe whole area at once: elements are ordered and reference each other by position,\nso a partial write would leave the caller reasoning about the order of operations.\n\n**Required scope:** `elements:write` — Change homepage, footer and sidebar elements",
        "operationId": "elementsReplace",
        "parameters": [
          {
            "name": "area",
            "in": "path",
            "description": "The element area: `homepage`, `footer` or `sidebar`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "footer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "elements"
                ],
                "properties": {
                  "elements": {
                    "description": "The whole area, in render order. Every element needs a `type`; `sort_order` defaults to the position in the array.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "text",
                        "content": "<p>Wheelhouse Ltd, 12 Baker Street, London</p>",
                        "background_color": "#f6f6f7",
                        "text_color": "#202223",
                        "background": null,
                        "sort_order": 1,
                        "row_order": 1
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "elements": [
                  {
                    "type": "text",
                    "content": "<p>Wheelhouse Ltd, 12 Baker Street, London</p>",
                    "background_color": "#f6f6f7",
                    "text_color": "#202223",
                    "background": null,
                    "sort_order": 1,
                    "row_order": 1
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored elements",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "area": "footer",
                  "elements": [
                    {
                      "type": "text",
                      "content": "<p>Wheelhouse Ltd, 12 Baker Street, London</p>",
                      "background_color": "#f6f6f7",
                      "text_color": "#202223",
                      "background": null,
                      "sort_order": 1,
                      "row_order": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"elements:write\" scope, which this app was not granted.",
                    "scope": "elements:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That element area does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That element area does not exist on this site.",
                    "resource": "element area"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"elements\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"elements\" is required.",
                    "field": "elements"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "elements:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/mail/accounts": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "List mailboxes",
        "description": "**Required scope:** `mail:read` — Read mailboxes and mailbox slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailList",
        "responses": {
          "200": {
            "description": "The mailboxes of this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "mailboxes": [
                    {
                      "uuid": "0192c593-4f18-7a62-b9d7-3081ea562c94",
                      "address": "sales@wheelhouse.nl",
                      "status": 20,
                      "provider": "qboxmail",
                      "password_change_supported": true,
                      "created": "2026-04-07T13:55:11+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:read\" scope, which this app was not granted.",
                    "scope": "mail:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Mail"
        ],
        "summary": "Create a mailbox",
        "description": "Provision a mailbox.\n\nWithin the free slots this needs mail:write. Beyond them it adds a EUR 1/month\nsubscription, which is spending, so it needs mail:order - without it the request\nstops at 409 rather than quietly costing the merchant money.\n\n**Required scope:** `mail:write` — Create, change and delete mailboxes within the free slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "local_part"
                ],
                "properties": {
                  "local_part": {
                    "description": "What goes before the @. Letters, digits and . - _ + only.",
                    "type": "string",
                    "example": "sales"
                  },
                  "domain": {
                    "description": "Which domain of the site to create the mailbox on. The site's primary domain is used when it is left out.",
                    "type": "string",
                    "example": "wheelhouse.nl"
                  }
                },
                "type": "object"
              },
              "example": {
                "local_part": "sales",
                "domain": "wheelhouse.nl"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The provisioned mailbox and its one-time password",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "mailbox": {
                    "uuid": "0192c593-4f18-7a62-b9d7-3081ea562c94",
                    "address": "sales@wheelhouse.nl",
                    "status": 20,
                    "provider": "qboxmail",
                    "password_change_supported": true,
                    "created": "2026-04-07T13:55:11+00:00"
                  },
                  "password": "f3a91c07d24b8e65",
                  "charged": false
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:write\" scope, which this app was not granted.",
                    "scope": "mail:write"
                  }
                }
              }
            }
          },
          "409": {
            "description": "All mailbox slots on this site are in use. Creating another one is charged monthly and needs the \"mail:order\" scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "mail_slot_required",
                    "message": "All mailbox slots on this site are in use. Creating another one is charged monthly and needs the \"mail:order\" scope.",
                    "used": 3,
                    "total": 3,
                    "scope": "mail:order"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"local_part\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"local_part\" is required.",
                    "field": "local_part"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/media": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "List media",
        "description": "**Required scope:** `media:read` — Read the media library",
        "operationId": "mediaList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the file name and alt text.",
            "required": false,
            "schema": {
              "description": "Free-text search over the file name and alt text.",
              "type": "string",
              "example": "road bike"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only files of this kind.",
            "required": false,
            "schema": {
              "description": "Only files of this kind.",
              "type": "string",
              "enum": [
                "image",
                "document",
                "audio",
                "video"
              ],
              "example": "image"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of media items",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "file_name": "road-bike-blue.jpg",
                      "file_type": "image",
                      "mime_type": "image/jpeg",
                      "file_size": 284113,
                      "alt_text": "Blue road bike against a white wall",
                      "url": "https://cdn.sitepack.io/wheelhouse/road-bike-blue.jpg",
                      "created": "2026-01-22T12:40:18+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 134,
                    "totalPages": 6
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"media:read\" scope, which this app was not granted.",
                    "scope": "media:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/modules": {
      "get": {
        "tags": [
          "Modules"
        ],
        "summary": "List active modules",
        "description": "List the modules active on this site.\n\n**Required scope:** `modules:read` — Read the site modules and their settings",
        "operationId": "modulesList",
        "responses": {
          "200": {
            "description": "The modules active on this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "modules": [
                    {
                      "key": "ContactForm",
                      "name": "Contact form",
                      "description": "Let visitors leave a message.",
                      "icon": "envelope"
                    },
                    {
                      "key": "OnlineStore",
                      "name": "Webshop",
                      "description": "Sell products from your site.",
                      "icon": "shopping-cart"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"modules:read\" scope, which this app was not granted.",
                    "scope": "modules:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "modules:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/navigations": {
      "get": {
        "tags": [
          "Navigation"
        ],
        "summary": "List navigations",
        "description": "**Required scope:** `navigation:read` — Read navigations and their items",
        "operationId": "navigationList",
        "responses": {
          "200": {
            "description": "Every navigation on the site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "navigations": [
                    {
                      "section": "main",
                      "name": "Hoofdmenu"
                    },
                    {
                      "section": "footer",
                      "name": "Footermenu"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:read\" scope, which this app was not granted.",
                    "scope": "navigation:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/orders": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "List orders",
        "description": "**Required scope:** `orders:read` — Read webshop orders, their items and payments",
        "operationId": "ordersList",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only orders in this status: 1 new, 2 on hold, 3 processing, 4 shipping, 5 cancelled, 6 refunded, 10 completed.",
            "required": false,
            "schema": {
              "description": "Only orders in this status: 1 new, 2 on hold, 3 processing, 4 shipping, 5 cancelled, 6 refunded, 10 completed.",
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4,
                5,
                6,
                10
              ],
              "example": 3
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "Only orders created on or after this date.",
            "required": false,
            "schema": {
              "description": "Only orders created on or after this date.",
              "type": "string",
              "example": "2026-06-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Only orders created on or before this date.",
            "required": false,
            "schema": {
              "description": "Only orders created on or before this date.",
              "type": "string",
              "example": "2026-06-30"
            }
          },
          {
            "name": "customer",
            "in": "query",
            "description": "Only orders of one customer, by customer uuid.",
            "required": false,
            "schema": {
              "description": "Only orders of one customer, by customer uuid.",
              "type": "string",
              "example": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of orders",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                      "order_number": "WH-2026-01184",
                      "status": 3,
                      "currency": "EUR",
                      "total_amount_cents": 129900,
                      "shipping_costs_cents": 0,
                      "customer_uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                      "comment": "Please deliver after 5 pm.",
                      "created": "2026-06-11T09:34:12+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 240,
                    "totalPages": 10
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"orders:read\" scope, which this app was not granted.",
                    "scope": "orders:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "orders:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Orders"
        ],
        "summary": "Create an order",
        "description": "Create an order from line items. Prices come from the catalogue - a price in the\nbody is ignored - and the order is created unpaid, at status \"new\".\n\n**Required scope:** `orders:write` — Create orders and change their status",
        "operationId": "ordersCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "items",
                  "customer"
                ],
                "properties": {
                  "items": {
                    "description": "The line items: `product_uuid` and `quantity` (at least 1). A product with option variants is ordered per variant: set `variant_uuid` (from GET /products/{uuid}/variants) to the size or colour to send; the variant's price and stock then apply. Prices come from the catalogue - a price sent here is ignored. Stock is taken as soon as the order is placed.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "product_uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                        "quantity": 1
                      },
                      {
                        "product_uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                        "variant_uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                        "quantity": 2
                      }
                    ]
                  },
                  "customer": {
                    "description": "Who the order is for. `email`, `first_name`, `last_name`, `street`, `number`, `postal_code`, `city` and `country_code` are required; `number_addition`, `phone_number`, `business`, `company_name` and `vat_number` are optional.",
                    "type": "object",
                    "example": {
                      "email": "emma@example.com",
                      "first_name": "Emma",
                      "last_name": "Clarke",
                      "street": "Baker Street",
                      "number": "12",
                      "number_addition": "A",
                      "postal_code": "NW1 6XE",
                      "city": "London",
                      "country_code": "GB",
                      "phone_number": "+44 20 7946 0958",
                      "business": false
                    }
                  },
                  "discount_code": {
                    "description": "A discount code to claim on the order. An unknown or spent code fails the request.",
                    "type": "string",
                    "example": "LENTE10"
                  },
                  "comment": {
                    "description": "The customer's own note, shown with the order in the admin.",
                    "type": "string",
                    "example": "Please deliver after 5 pm."
                  },
                  "delivery_method": {
                    "description": "The delivery method id, as configured on the site. Defaults to \"1\".",
                    "type": "string",
                    "example": "1"
                  },
                  "payment_method": {
                    "description": "Encoded as `gateway__method`, the method being a number. Defaults to `api__98` (manual invoice): the order is created unpaid, and taking money stays a PSP concern.",
                    "type": "string",
                    "example": "api__98"
                  }
                },
                "type": "object"
              },
              "example": {
                "items": [
                  {
                    "product_uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                    "quantity": 1
                  },
                  {
                    "product_uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                    "variant_uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                    "quantity": 2
                  }
                ],
                "customer": {
                  "email": "emma@example.com",
                  "first_name": "Emma",
                  "last_name": "Clarke",
                  "street": "Baker Street",
                  "number": "12",
                  "number_addition": "A",
                  "postal_code": "NW1 6XE",
                  "city": "London",
                  "country_code": "GB",
                  "phone_number": "+44 20 7946 0958",
                  "business": false
                },
                "discount_code": "LENTE10",
                "comment": "Please deliver after 5 pm.",
                "delivery_method": "1",
                "payment_method": "api__98"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "order": {
                    "uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                    "order_number": "WH-2026-01184",
                    "status": 3,
                    "currency": "EUR",
                    "total_amount_cents": 129900,
                    "shipping_costs_cents": 0,
                    "customer_uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "comment": "Please deliver after 5 pm.",
                    "created": "2026-06-11T09:34:12+00:00",
                    "items": [
                      {
                        "product_name": "Racefiets Aero 2026",
                        "product_barcode": "8712345678904",
                        "quantity": 1,
                        "price_cents": 129900,
                        "total_cents": 129900,
                        "variation_sku": null
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"orders:write\" scope, which this app was not granted.",
                    "scope": "orders:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "A product, or a variant of the line's product, does not exist"
          },
          "422": {
            "description": "Field \"items\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"items\" is required.",
                    "field": "items"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "orders:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/post-types": {
      "get": {
        "tags": [
          "Post types"
        ],
        "summary": "List post types",
        "description": "**Required scope:** `posts:read` — Read the post types installed apps add to the site, and their entries",
        "operationId": "postsListTypes",
        "responses": {
          "200": {
            "description": "Every post type on the site, with its fields",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "post_types": [
                    {
                      "key": "cinemas",
                      "name": "Cinemas",
                      "singular_name": "Cinema",
                      "app_uuid": "0192c3f0-4a1b-7e29-8d67-1c0b5a94f732",
                      "public": true,
                      "slug": "cinemas",
                      "sort": "title",
                      "entries_count": 12,
                      "fields": [
                        {
                          "key": "city",
                          "type": "text",
                          "label": "City",
                          "required": true,
                          "help": null,
                          "options": []
                        },
                        {
                          "key": "format",
                          "type": "options",
                          "label": "Format",
                          "required": false,
                          "help": null,
                          "options": [
                            {
                              "value": "standard",
                              "label": "Standard"
                            },
                            {
                              "value": "imax",
                              "label": "IMAX"
                            }
                          ]
                        },
                        {
                          "key": "screens",
                          "type": "number",
                          "label": "Screens",
                          "required": false,
                          "help": null,
                          "options": []
                        }
                      ],
                      "fields_schema": {
                        "type": "object",
                        "properties": {
                          "city": {
                            "field_type": "text",
                            "description": "City",
                            "type": "string",
                            "maxLength": 500,
                            "hint": "One line of plain text."
                          },
                          "format": {
                            "field_type": "options",
                            "description": "Format",
                            "type": [
                              "string",
                              "null"
                            ],
                            "enum": [
                              "standard",
                              "imax",
                              null
                            ],
                            "options": [
                              {
                                "value": "standard",
                                "label": "Standard"
                              },
                              {
                                "value": "imax",
                                "label": "IMAX"
                              }
                            ]
                          },
                          "screens": {
                            "field_type": "number",
                            "description": "Screens",
                            "type": [
                              "number",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "city"
                        ],
                        "additionalProperties": false
                      },
                      "archive": {
                        "post_type": "cinemas",
                        "url": "/cinemas",
                        "seo_title": "All our cinemas in the Netherlands",
                        "seo_description": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX.",
                        "focus_keyword": "cinemas",
                        "no_index": false,
                        "modified": "2026-10-07T10:15:00+02:00"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:read\" scope, which this app was not granted.",
                    "scope": "posts:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/post-types/{postType}": {
      "get": {
        "tags": [
          "Post types"
        ],
        "summary": "Get a post type",
        "description": "**Required scope:** `posts:read` — Read the post types installed apps add to the site, and their entries",
        "operationId": "postsGetType",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One post type, with its fields",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "post_type": {
                    "key": "cinemas",
                    "name": "Cinemas",
                    "singular_name": "Cinema",
                    "app_uuid": "0192c3f0-4a1b-7e29-8d67-1c0b5a94f732",
                    "public": true,
                    "slug": "cinemas",
                    "sort": "title",
                    "entries_count": 12,
                    "fields": [
                      {
                        "key": "city",
                        "type": "text",
                        "label": "City",
                        "required": true,
                        "help": null,
                        "options": []
                      },
                      {
                        "key": "format",
                        "type": "options",
                        "label": "Format",
                        "required": false,
                        "help": null,
                        "options": [
                          {
                            "value": "standard",
                            "label": "Standard"
                          },
                          {
                            "value": "imax",
                            "label": "IMAX"
                          }
                        ]
                      },
                      {
                        "key": "screens",
                        "type": "number",
                        "label": "Screens",
                        "required": false,
                        "help": null,
                        "options": []
                      }
                    ],
                    "fields_schema": {
                      "type": "object",
                      "properties": {
                        "city": {
                          "field_type": "text",
                          "description": "City",
                          "type": "string",
                          "maxLength": 500,
                          "hint": "One line of plain text."
                        },
                        "format": {
                          "field_type": "options",
                          "description": "Format",
                          "type": [
                            "string",
                            "null"
                          ],
                          "enum": [
                            "standard",
                            "imax",
                            null
                          ],
                          "options": [
                            {
                              "value": "standard",
                              "label": "Standard"
                            },
                            {
                              "value": "imax",
                              "label": "IMAX"
                            }
                          ]
                        },
                        "screens": {
                          "field_type": "number",
                          "description": "Screens",
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "city"
                      ],
                      "additionalProperties": false
                    },
                    "archive": {
                      "post_type": "cinemas",
                      "url": "/cinemas",
                      "seo_title": "All our cinemas in the Netherlands",
                      "seo_description": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX.",
                      "focus_keyword": "cinemas",
                      "no_index": false,
                      "modified": "2026-10-07T10:15:00+02:00"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:read\" scope, which this app was not granted.",
                    "scope": "posts:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/post-types/{postType}/archive": {
      "get": {
        "tags": [
          "Post types"
        ],
        "summary": "Get a post type overview page",
        "description": "**Required scope:** `posts:read` — Read the post types installed apps add to the site, and their entries",
        "operationId": "postsGetArchive",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The SEO settings of the post type's overview page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "archive": {
                    "post_type": "cinemas",
                    "url": "/cinemas",
                    "seo_title": "All our cinemas in the Netherlands",
                    "seo_description": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX.",
                    "focus_keyword": "cinemas",
                    "no_index": false,
                    "modified": "2026-10-07T10:15:00+02:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:read\" scope, which this app was not granted.",
                    "scope": "posts:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Post types"
        ],
        "summary": "Update a post type overview page",
        "description": "The page title, meta description, focus keyword and no-index of the overview page\n(`/<slug>`). Only what is sent changes; an empty string clears a text.\n\n**Required scope:** `posts:write` — Create, change and delete entries of the post types installed apps add",
        "operationId": "postsUpdateArchive",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "seo_title": {
                    "description": "The title for search engines. An empty string clears it, the overview then uses the post type name.",
                    "type": "string",
                    "example": "All our cinemas in the Netherlands"
                  },
                  "seo_description": {
                    "description": "The meta description. An empty string clears it.",
                    "type": "string",
                    "example": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX."
                  },
                  "focus_keyword": {
                    "description": "The SEO focus keyword.",
                    "type": "string",
                    "example": "cinemas"
                  },
                  "no_index": {
                    "description": "Keep the overview page out of search engines and the sitemap.",
                    "type": "boolean",
                    "example": false
                  }
                },
                "type": "object"
              },
              "example": {
                "seo_title": "All our cinemas in the Netherlands",
                "seo_description": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX.",
                "focus_keyword": "cinemas",
                "no_index": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated SEO settings of the overview page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "archive": {
                    "post_type": "cinemas",
                    "url": "/cinemas",
                    "seo_title": "All our cinemas in the Netherlands",
                    "seo_description": "Find a cinema near you: twelve locations, from Arnhem to Zwolle, with IMAX and 4DX.",
                    "focus_keyword": "cinemas",
                    "no_index": false,
                    "modified": "2026-10-07T10:15:00+02:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:write\" scope, which this app was not granted.",
                    "scope": "posts:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"seo_title\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"seo_title\" cannot be empty.",
                    "field": "seo_title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/post-types/{postType}/entries": {
      "get": {
        "tags": [
          "Post types"
        ],
        "summary": "List post entries",
        "description": "**Required scope:** `posts:read` — Read the post types installed apps add to the site, and their entries",
        "operationId": "postsListEntries",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the entry title and slug.",
            "required": false,
            "schema": {
              "description": "Free-text search over the entry title and slug.",
              "type": "string",
              "example": "arnhem"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of entries of the post type",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "post_type": "cinemas",
                      "uuid": "0192c61b-5c27-7f38-8a46-17b0d8e942c3",
                      "title": "Vue Arnhem",
                      "slug": "vue-arnhem",
                      "url": "/cinemas/vue-arnhem",
                      "visible": true,
                      "position": 0,
                      "fields": {
                        "city": "Arnhem",
                        "format": "imax",
                        "screens": 9
                      },
                      "seo_title": null,
                      "seo_description": null,
                      "focus_keyword": null,
                      "no_index": false,
                      "modified": "2026-09-30T10:00:00+00:00",
                      "created": "2026-09-30T10:00:00+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 12,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:read\" scope, which this app was not granted.",
                    "scope": "posts:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Post types"
        ],
        "summary": "Create a post entry",
        "description": "**Required scope:** `posts:write` — Create, change and delete entries of the post types installed apps add",
        "operationId": "postsCreateEntry",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "description": "The entry title.",
                    "type": "string",
                    "example": "Vue Arnhem"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out, and made unique per post type.",
                    "type": "string",
                    "example": "vue-arnhem"
                  },
                  "fields": {
                    "description": "Field key => value, following the `fields_schema` of the post type. Required fields must be filled.",
                    "type": "object",
                    "example": {
                      "city": "Arnhem",
                      "format": "imax",
                      "screens": 9
                    }
                  },
                  "visible": {
                    "description": "Whether the entry shows on the website. New entries are visible.",
                    "type": "boolean",
                    "example": true
                  },
                  "position": {
                    "description": "Sort position, for post types that sort by position.",
                    "type": "integer",
                    "example": 0
                  },
                  "seo_title": {
                    "description": "The title for search engines. An empty string clears it.",
                    "type": "string",
                    "example": "Vue Arnhem - cinema in the city centre"
                  },
                  "seo_description": {
                    "description": "The meta description. An empty string clears it.",
                    "type": "string",
                    "example": "Nine screens, including IMAX, next to Arnhem Central station."
                  },
                  "focus_keyword": {
                    "description": "The SEO focus keyword.",
                    "type": "string",
                    "example": "cinema arnhem"
                  },
                  "no_index": {
                    "description": "Keep this entry out of search engines.",
                    "type": "boolean",
                    "example": false
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Vue Arnhem",
                "slug": "vue-arnhem",
                "fields": {
                  "city": "Arnhem",
                  "format": "imax",
                  "screens": 9
                },
                "visible": true,
                "position": 0,
                "seo_title": "Vue Arnhem - cinema in the city centre",
                "seo_description": "Nine screens, including IMAX, next to Arnhem Central station.",
                "focus_keyword": "cinema arnhem",
                "no_index": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created entry",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "entry": {
                    "post_type": "cinemas",
                    "uuid": "0192c61b-5c27-7f38-8a46-17b0d8e942c3",
                    "title": "Vue Arnhem",
                    "slug": "vue-arnhem",
                    "url": "/cinemas/vue-arnhem",
                    "visible": true,
                    "position": 0,
                    "fields": {
                      "city": "Arnhem",
                      "format": "imax",
                      "screens": 9
                    },
                    "seo_title": null,
                    "seo_description": null,
                    "focus_keyword": null,
                    "no_index": false,
                    "modified": "2026-09-30T10:00:00+00:00",
                    "created": "2026-09-30T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:write\" scope, which this app was not granted.",
                    "scope": "posts:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" is required.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/post-types/{postType}/entries/{uuid}": {
      "get": {
        "tags": [
          "Post types"
        ],
        "summary": "Get a post entry",
        "description": "**Required scope:** `posts:read` — Read the post types installed apps add to the site, and their entries",
        "operationId": "postsGetEntry",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the post-type. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One entry",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "entry": {
                    "post_type": "cinemas",
                    "uuid": "0192c61b-5c27-7f38-8a46-17b0d8e942c3",
                    "title": "Vue Arnhem",
                    "slug": "vue-arnhem",
                    "url": "/cinemas/vue-arnhem",
                    "visible": true,
                    "position": 0,
                    "fields": {
                      "city": "Arnhem",
                      "format": "imax",
                      "screens": 9
                    },
                    "seo_title": null,
                    "seo_description": null,
                    "focus_keyword": null,
                    "no_index": false,
                    "modified": "2026-09-30T10:00:00+00:00",
                    "created": "2026-09-30T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:read\" scope, which this app was not granted.",
                    "scope": "posts:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Post types"
        ],
        "summary": "Delete a post entry",
        "description": "**Required scope:** `posts:write` — Create, change and delete entries of the post types installed apps add",
        "operationId": "postsDeleteEntry",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the post-type. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The entry was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:write\" scope, which this app was not granted.",
                    "scope": "posts:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Post types"
        ],
        "summary": "Update a post entry",
        "description": "Only what is sent changes: a field left out of \"fields\" keeps its value, a field\nsent as null is cleared.\n\n**Required scope:** `posts:write` — Create, change and delete entries of the post types installed apps add",
        "operationId": "postsUpdateEntry",
        "parameters": [
          {
            "name": "postType",
            "in": "path",
            "description": "The post type key, as returned by `GET /post-types`.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "[a-z0-9-]+",
              "example": "cinemas"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the post-type. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "title": {
                    "description": "The entry title.",
                    "type": "string",
                    "example": "Vue Arnhem"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out, and made unique per post type.",
                    "type": "string",
                    "example": "vue-arnhem"
                  },
                  "fields": {
                    "description": "Field key => value, following the `fields_schema` of the post type. A field left out keeps its value; a field sent as null is cleared.",
                    "type": "object",
                    "example": {
                      "city": "Arnhem",
                      "format": "imax",
                      "screens": 9
                    }
                  },
                  "visible": {
                    "description": "Whether the entry shows on the website. New entries are visible.",
                    "type": "boolean",
                    "example": true
                  },
                  "position": {
                    "description": "Sort position, for post types that sort by position.",
                    "type": "integer",
                    "example": 0
                  },
                  "seo_title": {
                    "description": "The title for search engines. An empty string clears it.",
                    "type": "string",
                    "example": "Vue Arnhem - cinema in the city centre"
                  },
                  "seo_description": {
                    "description": "The meta description. An empty string clears it.",
                    "type": "string",
                    "example": "Nine screens, including IMAX, next to Arnhem Central station."
                  },
                  "focus_keyword": {
                    "description": "The SEO focus keyword.",
                    "type": "string",
                    "example": "cinema arnhem"
                  },
                  "no_index": {
                    "description": "Keep this entry out of search engines.",
                    "type": "boolean",
                    "example": false
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Vue Arnhem",
                "slug": "vue-arnhem",
                "fields": {
                  "city": "Arnhem",
                  "format": "imax",
                  "screens": 9
                },
                "visible": true,
                "position": 0,
                "seo_title": "Vue Arnhem - cinema in the city centre",
                "seo_description": "Nine screens, including IMAX, next to Arnhem Central station.",
                "focus_keyword": "cinema arnhem",
                "no_index": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated entry",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "entry": {
                    "post_type": "cinemas",
                    "uuid": "0192c61b-5c27-7f38-8a46-17b0d8e942c3",
                    "title": "Vue Arnhem",
                    "slug": "vue-arnhem",
                    "url": "/cinemas/vue-arnhem",
                    "visible": true,
                    "position": 0,
                    "fields": {
                      "city": "Arnhem",
                      "format": "imax",
                      "screens": 9
                    },
                    "seo_title": null,
                    "seo_description": null,
                    "focus_keyword": null,
                    "no_index": false,
                    "modified": "2026-09-30T10:00:00+00:00",
                    "created": "2026-09-30T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"posts:write\" scope, which this app was not granted.",
                    "scope": "posts:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That post-type does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That post-type does not exist on this site.",
                    "resource": "post-type"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" cannot be empty.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "posts:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List products",
        "description": "**Required scope:** `products:read` — Read products, variants, images and bundles",
        "operationId": "productsList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over name, description and brand.",
            "required": false,
            "schema": {
              "description": "Free-text search over name, description and brand.",
              "type": "string",
              "example": "road bike"
            }
          },
          {
            "name": "sku",
            "in": "query",
            "description": "Exact match on the SKU.",
            "required": false,
            "schema": {
              "description": "Exact match on the SKU.",
              "type": "string",
              "example": "RF-2026-BLUE-54"
            }
          },
          {
            "name": "barcode",
            "in": "query",
            "description": "Exact match on the barcode (EAN/UPC).",
            "required": false,
            "schema": {
              "description": "Exact match on the barcode (EAN/UPC).",
              "type": "string",
              "example": "8712345678904"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of products",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                      "name": "Racefiets Aero 2026",
                      "slug": "road-bike-aero-2026",
                      "sku": "RF-2026-BLUE-54",
                      "barcode": "8712345678904",
                      "brand": "Wheelhouse",
                      "model": "Aero",
                      "description": "Light carbon road bike with Shimano 105.",
                      "currency": "EUR",
                      "price_cents": 129900,
                      "price_promo_cents": 119900,
                      "stock": 7,
                      "archived": false,
                      "seo": {
                        "meta_description": "Light carbon road bike with Shimano 105.",
                        "focus_keyword": "carbon road bike"
                      },
                      "created": "2026-01-15T11:02:38+00:00",
                      "modified": "2026-06-11T09:31:04+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 318,
                    "totalPages": 13
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:read\" scope, which this app was not granted.",
                    "scope": "products:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Create a product",
        "description": "**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "name",
                  "price_cents"
                ],
                "properties": {
                  "name": {
                    "description": "The product name.",
                    "type": "string",
                    "example": "Racefiets Aero 2026"
                  },
                  "price_cents": {
                    "description": "The regular price in cents, excluding nothing - it is what the shop shows. Cannot be negative.",
                    "type": "integer",
                    "example": 129900
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out.",
                    "type": "string",
                    "example": "road-bike-aero-2026"
                  },
                  "sku": {
                    "description": "Your own article number.",
                    "type": "string",
                    "example": "RF-2026-BLUE-54"
                  },
                  "barcode": {
                    "description": "The EAN or UPC.",
                    "type": "string",
                    "example": "8712345678904"
                  },
                  "brand": {
                    "description": "The brand shown on the product page.",
                    "type": "string",
                    "example": "Wheelhouse"
                  },
                  "model": {
                    "description": "The model name.",
                    "type": "string",
                    "example": "Aero"
                  },
                  "description": {
                    "description": "The product description. HTML is allowed.",
                    "type": "string",
                    "example": "Light carbon road bike with Shimano 105."
                  },
                  "price_promo_cents": {
                    "description": "A sale price in cents, shown struck through against `price_cents`. `null` ends the sale.",
                    "type": "integer",
                    "example": 119900
                  },
                  "stock": {
                    "description": "The absolute stock level. Use `POST /products/{uuid}/stock/adjust` for day-to-day movements: a delta does not overwrite what another app just wrote.",
                    "type": "integer",
                    "example": 7
                  },
                  "archived": {
                    "description": "Archived products stay for existing orders but leave the shop.",
                    "type": "boolean",
                    "example": false
                  },
                  "seo": {
                    "description": "Search-engine metadata: `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "meta_description": "Light carbon road bike with Shimano 105.",
                      "focus_keyword": "carbon road bike"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefiets Aero 2026",
                "price_cents": 129900,
                "slug": "road-bike-aero-2026",
                "sku": "RF-2026-BLUE-54",
                "barcode": "8712345678904",
                "brand": "Wheelhouse",
                "model": "Aero",
                "description": "Light carbon road bike with Shimano 105.",
                "price_promo_cents": 119900,
                "stock": 7,
                "archived": false,
                "seo": {
                  "meta_description": "Light carbon road bike with Shimano 105.",
                  "focus_keyword": "carbon road bike"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created product",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "product": {
                    "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                    "name": "Racefiets Aero 2026",
                    "slug": "road-bike-aero-2026",
                    "sku": "RF-2026-BLUE-54",
                    "barcode": "8712345678904",
                    "brand": "Wheelhouse",
                    "model": "Aero",
                    "description": "Light carbon road bike with Shimano 105.",
                    "currency": "EUR",
                    "price_cents": 129900,
                    "price_promo_cents": 119900,
                    "stock": 7,
                    "archived": false,
                    "seo": {
                      "meta_description": "Light carbon road bike with Shimano 105.",
                      "focus_keyword": "carbon road bike"
                    },
                    "created": "2026-01-15T11:02:38+00:00",
                    "modified": "2026-06-11T09:31:04+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" is required.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/redirects": {
      "get": {
        "tags": [
          "Redirects"
        ],
        "summary": "List redirects",
        "description": "**Required scope:** `redirects:read` — Read URL redirects",
        "operationId": "redirectsList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over both sides of the redirect.",
            "required": false,
            "schema": {
              "description": "Free-text search over both sides of the redirect.",
              "type": "string",
              "example": "oude-"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of redirects",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4f9-59ba-7e37-97d4-1a8c6b230e5f",
                      "from": "/oude-pagina",
                      "to": "/about-us",
                      "type": 301,
                      "hits": 214
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 63,
                    "totalPages": 3
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"redirects:read\" scope, which this app was not granted.",
                    "scope": "redirects:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "redirects:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Redirects"
        ],
        "summary": "Create a redirect",
        "description": "**Required scope:** `redirects:write` — Create, change and delete URL redirects",
        "operationId": "redirectsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "from",
                  "to"
                ],
                "properties": {
                  "from": {
                    "description": "The path to redirect away from, relative to the site root.",
                    "type": "string",
                    "example": "/oude-pagina"
                  },
                  "to": {
                    "description": "Where to send the visitor: a site-relative path or an absolute URL.",
                    "type": "string",
                    "example": "/about-us"
                  },
                  "type": {
                    "description": "The HTTP status: 301 (permanent, the default) or 302 (temporary).",
                    "type": "integer",
                    "enum": [
                      301,
                      302
                    ],
                    "example": 301
                  }
                },
                "type": "object"
              },
              "example": {
                "from": "/oude-pagina",
                "to": "/about-us",
                "type": 301
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created redirect",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "redirect": {
                    "uuid": "0192c4f9-59ba-7e37-97d4-1a8c6b230e5f",
                    "from": "/oude-pagina",
                    "to": "/about-us",
                    "type": 301,
                    "hits": 214
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"redirects:write\" scope, which this app was not granted.",
                    "scope": "redirects:write"
                  }
                }
              }
            }
          },
          "409": {
            "description": "A redirect from \"/oude-pagina\" already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "redirect_exists",
                    "message": "A redirect from \"/oude-pagina\" already exists."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"from\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"from\" is required.",
                    "field": "from"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "redirects:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/shipping-labels": {
      "get": {
        "tags": [
          "Shipping"
        ],
        "summary": "List shipping labels",
        "description": "List shipping labels, optionally for one order.\n\n**Required scope:** `shipping:read` — Read shipping labels and their track and trace codes",
        "operationId": "shippingList",
        "parameters": [
          {
            "name": "order",
            "in": "query",
            "description": "Only labels of one order, by order uuid.",
            "required": false,
            "schema": {
              "description": "Only labels of one order, by order uuid.",
              "type": "string",
              "example": "0192c52c-1a7f-7d63-84be-25c908f7b361"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of shipping labels",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c55f-7d13-7b84-9f52-08ea6c491d37",
                      "order_uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                      "provider": "sendcloud",
                      "carrier": "PostNL",
                      "shipping_method": "PostNL Standaard",
                      "track_trace_code": "3SABCD1234567",
                      "created": "2026-06-12T07:14:29+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 96,
                    "totalPages": 4
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"shipping:read\" scope, which this app was not granted.",
                    "scope": "shipping:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "shipping:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/site": {
      "get": {
        "tags": [
          "Site"
        ],
        "summary": "Read the site settings",
        "description": "**Required scope:** `sites:read` — Read the site settings, notices and brand book",
        "operationId": "siteGet",
        "responses": {
          "200": {
            "description": "The site this token belongs to",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "site": {
                    "uuid": "0192c3e1-9d52-7b08-a4f3-6e17c025b8d9",
                    "name": "Wheelhouse",
                    "domain": "wheelhouse.nl",
                    "protocol": "https",
                    "locale": "en"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"sites:read\" scope, which this app was not granted.",
                    "scope": "sites:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "sites:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Site"
        ],
        "summary": "Update the site settings",
        "description": "**Required scope:** `sites:write` — Change the site settings, notices and brand book",
        "operationId": "siteUpdate",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "name": {
                    "description": "The site name, as shown in the admin.",
                    "type": "string",
                    "example": "Wheelhouse"
                  },
                  "locale": {
                    "description": "The default locale of the site. Translations hang off this one.",
                    "type": "string",
                    "example": "en"
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Wheelhouse",
                "locale": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "site": {
                    "uuid": "0192c3e1-9d52-7b08-a4f3-6e17c025b8d9",
                    "name": "Wheelhouse",
                    "locale": "en"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"sites:write\" scope, which this app was not granted.",
                    "scope": "sites:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" cannot be empty.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "sites:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/site/social-media": {
      "get": {
        "tags": [
          "Site"
        ],
        "summary": "Read the social media profiles",
        "description": "**Required scope:** `sites:read` — Read the site settings, notices and brand book",
        "operationId": "siteSocialMedia",
        "responses": {
          "200": {
            "description": "The social media profiles of the site: one URL per network (facebook, twitter, instagram, youtube, pinterest, tiktok, linkedin), null when not linked",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "social_media": {
                    "facebook": "https://www.facebook.com/wheelhouse",
                    "twitter": null,
                    "instagram": "https://www.instagram.com/wheelhouse",
                    "youtube": null,
                    "pinterest": null,
                    "tiktok": null,
                    "linkedin": "https://www.linkedin.com/company/wheelhouse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"sites:read\" scope, which this app was not granted.",
                    "scope": "sites:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "sites:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Site"
        ],
        "summary": "Update the social media profiles",
        "description": "Send only the networks to change; the others keep their URL, and null or an empty\nstring unlinks a network. A URL that is not on the network's own domain is refused\nwith a 422 and nothing is changed.\n\n**Required scope:** `sites:write` — Change the site settings, notices and brand book",
        "operationId": "siteUpdateSocialMedia",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "facebook": {
                    "description": "The Facebook profile URL, on facebook.com or fb.com. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.facebook.com/wheelhouse"
                  },
                  "twitter": {
                    "description": "The X / Twitter profile URL, on x.com or twitter.com. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.x.com/wheelhouse"
                  },
                  "instagram": {
                    "description": "The Instagram profile URL, on instagram.com. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.instagram.com/wheelhouse"
                  },
                  "youtube": {
                    "description": "The YouTube profile URL, on youtube.com or youtu.be. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.youtube.com/@wheelhouse"
                  },
                  "pinterest": {
                    "description": "The Pinterest profile URL, on pinterest.com, a country domain such as pinterest.nl, or pin.it. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.pinterest.com/wheelhouse"
                  },
                  "tiktok": {
                    "description": "The TikTok profile URL, on tiktok.com. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.tiktok.com/@wheelhouse"
                  },
                  "linkedin": {
                    "description": "The LinkedIn profile URL, on linkedin.com. null or \"\" unlinks it.",
                    "type": "string",
                    "example": "https://www.linkedin.com/company/wheelhouse"
                  }
                },
                "type": "object"
              },
              "example": {
                "facebook": "https://www.facebook.com/wheelhouse",
                "twitter": "https://www.x.com/wheelhouse",
                "instagram": "https://www.instagram.com/wheelhouse",
                "youtube": "https://www.youtube.com/@wheelhouse",
                "pinterest": "https://www.pinterest.com/wheelhouse",
                "tiktok": "https://www.tiktok.com/@wheelhouse",
                "linkedin": "https://www.linkedin.com/company/wheelhouse"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every social media profile of the site after the change",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "social_media": {
                    "facebook": "https://www.facebook.com/wheelhouse",
                    "twitter": null,
                    "instagram": "https://www.instagram.com/wheelhouse",
                    "youtube": null,
                    "pinterest": null,
                    "tiktok": null,
                    "linkedin": "https://www.linkedin.com/company/wheelhouse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"sites:write\" scope, which this app was not granted.",
                    "scope": "sites:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "An unknown network, or a URL that is not a profile of that network",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"facebook\" cannot be empty.",
                    "field": "facebook"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "sites:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/leads": {
      "get": {
        "tags": [
          "Leads"
        ],
        "summary": "List leads",
        "description": "**Required scope:** `leads:read` — Read form submissions and their notes",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteLeads",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only submissions in this status: 1 new, 2 in contact, 3 waiting on the lead, 4 won, 5 lost.",
            "required": false,
            "schema": {
              "description": "Only submissions in this status: 1 new, 2 in contact, 3 waiting on the lead, 4 won, 5 lost.",
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4,
                5
              ],
              "example": 1
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only submissions of this kind: 1 contact form, 2 newsletter, 3 product enquiry, 4 other.",
            "required": false,
            "schema": {
              "description": "Only submissions of this kind: 1 contact form, 2 newsletter, 3 product enquiry, 4 other.",
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4
              ],
              "example": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of form submissions",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c560-3e91-7c25-86ab-1d47f0b93e58",
                      "type": 1,
                      "status": 1,
                      "source": "contact_form",
                      "subject": "Test ride",
                      "message": "Hi, I would like to test ride the Aero 2026 in size L this Saturday. Is that possible?",
                      "email": "emma@example.com",
                      "first_name": "Emma",
                      "last_name": "Clarke",
                      "company_name": null,
                      "job_title": null,
                      "phone": "+44 20 7946 0958",
                      "address": "12 Baker Street",
                      "zip": "NW1 6XE",
                      "city": "London",
                      "country": "GB",
                      "language": "en",
                      "rating": 2,
                      "value_cents": null,
                      "created": "2026-06-12T10:58:03+00:00",
                      "updated": "2026-06-12T10:58:03+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 87,
                    "totalPages": 4
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:read\" scope, which this app was not granted.",
                    "scope": "leads:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/billing/invoices": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "List invoices",
        "description": "The site's invoices.\n\n**Required scope:** `billing:read` — Read the subscription, plan and invoices",
        "externalDocs": {
          "description": "Invoices",
          "url": "https://help.sitepack.eu/docs/admin/settings/invoices"
        },
        "operationId": "siteInvoices",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of invoices",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "invoice_number": "2026-004182",
                      "currency": "EUR",
                      "amount_cents": 1500,
                      "total_amount_cents": 1815,
                      "paid": "2026-06-01T04:11:09+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 24,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"billing:read\" scope, which this app was not granted.",
                    "scope": "billing:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "billing:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/comments": {
      "get": {
        "tags": [
          "Comments"
        ],
        "summary": "List comments",
        "description": "**Required scope:** `comments:read` — Read comments",
        "operationId": "siteComments",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only comments in this state: 0 awaiting moderation, 1 approved, 2 rejected.",
            "required": false,
            "schema": {
              "description": "Only comments in this state: 0 awaiting moderation, 1 approved, 2 rejected.",
              "type": "integer",
              "enum": [
                0,
                1,
                2
              ],
              "example": 0
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of comments",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c571-5a26-7e70-93cd-4f18b2ea607d",
                      "author_name": "Jeroen",
                      "title": "Great bike",
                      "comment": "Rides beautifully, even on long trips.",
                      "rating": true,
                      "status": 0,
                      "created": "2026-06-09T19:22:41+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 51,
                    "totalPages": 3
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"comments:read\" scope, which this app was not granted.",
                    "scope": "comments:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "comments:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/statistics": {
      "get": {
        "tags": [
          "Site"
        ],
        "summary": "Read site statistics",
        "description": "**Required scope:** `statistics:read` — Read site statistics and conversions",
        "externalDocs": {
          "description": "The dashboard",
          "url": "https://help.sitepack.eu/docs/admin/dashboard"
        },
        "operationId": "siteStatistics",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "First day to report on. Defaults to 30 days ago.",
            "required": false,
            "schema": {
              "description": "First day to report on. Defaults to 30 days ago.",
              "type": "string",
              "example": "2026-06-01"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Last day to report on. Defaults to today.",
            "required": false,
            "schema": {
              "description": "Last day to report on. Defaults to today.",
              "type": "string",
              "example": "2026-06-03"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Daily page views and unique visitors",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "days": [
                    {
                      "day": "2026-06-01",
                      "views": 1284,
                      "visitors": 903
                    },
                    {
                      "day": "2026-06-02",
                      "views": 1411,
                      "visitors": 1002
                    },
                    {
                      "day": "2026-06-03",
                      "views": 998,
                      "visitors": 742
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"statistics:read\" scope, which this app was not granted.",
                    "scope": "statistics:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "statistics:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/billing/subscriptions": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "List subscriptions",
        "description": "**Required scope:** `billing:read` — Read the subscription, plan and invoices",
        "externalDocs": {
          "description": "Subscription and plan",
          "url": "https://help.sitepack.eu/docs/admin/settings/subscription"
        },
        "operationId": "siteBilling",
        "responses": {
          "200": {
            "description": "The running subscriptions of this site",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "subscriptions": [
                    {
                      "type": "sp_2026_shop",
                      "price": 25,
                      "quantity": 1,
                      "renewal_interval": "+1 month",
                      "status": 10,
                      "start_date": "2026-01-01T00:00:00+00:00",
                      "end_date": null,
                      "renewal_date": "2026-08-01T00:00:00+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"billing:read\" scope, which this app was not granted.",
                    "scope": "billing:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "billing:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/tags": {
      "get": {
        "tags": [
          "Tags"
        ],
        "summary": "List tags",
        "description": "**Required scope:** `tags:read` — Read tags",
        "operationId": "tagsList",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free-text search over the tag name and slug.",
            "required": false,
            "schema": {
              "description": "Free-text search over the tag name and slug.",
              "type": "string",
              "example": "maintenance"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "example": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of tags",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c4e8-2f43-7c1d-8a05-93b6ed17f204",
                      "slug": "maintenance",
                      "name": "Onderhoud",
                      "description": "Everything about looking after your bike.",
                      "created": "2025-09-14T07:31:56+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 27,
                    "totalPages": 2
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"tags:read\" scope, which this app was not granted.",
                    "scope": "tags:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "tags:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Tags"
        ],
        "summary": "Create a tag",
        "description": "**Required scope:** `tags:write` — Create, change and delete tags",
        "operationId": "tagsCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "description": "The tag as a reader sees it.",
                    "type": "string",
                    "example": "Onderhoud"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out, and unique per site.",
                    "type": "string",
                    "example": "maintenance"
                  },
                  "description": {
                    "description": "Shown on the tag overview page.",
                    "type": "string",
                    "example": "Everything about looking after your bike."
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Onderhoud",
                "slug": "maintenance",
                "description": "Everything about looking after your bike."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created tag",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "tag": {
                    "uuid": "0192c4e8-2f43-7c1d-8a05-93b6ed17f204",
                    "slug": "maintenance",
                    "name": "Onderhoud",
                    "description": "Everything about looking after your bike.",
                    "created": "2025-09-14T07:31:56+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"tags:write\" scope, which this app was not granted.",
                    "scope": "tags:write"
                  }
                }
              }
            }
          },
          "409": {
            "description": "A tag with the slug \"maintenance\" already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "tag_exists",
                    "message": "A tag with the slug \"maintenance\" already exists."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" is required.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "tags:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/theme": {
      "get": {
        "tags": [
          "Theme"
        ],
        "summary": "Read the active theme",
        "description": "Read the active theme.\n\n**Required scope:** `themes:read` — Read the active theme, its layouts and its design settings",
        "operationId": "themeGet",
        "responses": {
          "200": {
            "description": "The theme this site renders with",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "theme": {
                    "name": "Modern",
                    "background_color": "#ffffff",
                    "background_image": null
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"themes:read\" scope, which this app was not granted.",
                    "scope": "themes:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "themes:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/usps": {
      "get": {
        "tags": [
          "USPs"
        ],
        "summary": "List USPs",
        "description": "**Required scope:** `usp:read` — Read the site USPs (unique selling points)",
        "operationId": "uspList",
        "responses": {
          "200": {
            "description": "Every USP on the site, in order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "usps": [
                    {
                      "uuid": "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                      "title": "Free shipping",
                      "text": "Vanaf €50 in Nederland",
                      "icon": "truck",
                      "visible": true,
                      "derive_shipping": false,
                      "sort_order": 10
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:read\" scope, which this app was not granted.",
                    "scope": "usp:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "USPs"
        ],
        "summary": "Reorder USPs",
        "description": "Reorder the whole list in one call: the \"usps\" array of uuids is the new order.\n\nNothing is written unless every uuid resolves, so a typo cannot half-reorder the list.\n\n**Required scope:** `usp:write` — Create, change, reorder and delete USPs",
        "operationId": "uspReorder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "usps"
                ],
                "properties": {
                  "usps": {
                    "description": "The USP uuids in their new order. Every USP has to be listed; nothing is written unless all of them resolve.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                      "0192c5b6-08d7-7f23-8c41-7a90bd52e1c8"
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "usps": [
                  "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                  "0192c5b6-08d7-7f23-8c41-7a90bd52e1c8"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reordered USPs",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "usps": [
                    {
                      "uuid": "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                      "title": "Free shipping",
                      "text": "Vanaf €50 in Nederland",
                      "icon": "truck",
                      "visible": true,
                      "derive_shipping": false,
                      "sort_order": 10
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:write\" scope, which this app was not granted.",
                    "scope": "usp:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"usps\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"usps\" is required.",
                    "field": "usps"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:write"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "USPs"
        ],
        "summary": "Create a USP",
        "description": "**Required scope:** `usp:write` — Create, change, reorder and delete USPs",
        "operationId": "uspCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "description": "The USP heading.",
                    "type": "string",
                    "example": "Free shipping"
                  },
                  "text": {
                    "description": "The supporting line under the title.",
                    "type": "string",
                    "example": "Vanaf €50 in Nederland"
                  },
                  "icon": {
                    "description": "The icon name the theme renders.",
                    "type": "string",
                    "example": "truck"
                  },
                  "visible": {
                    "description": "Whether the theme shows this USP.",
                    "type": "boolean",
                    "example": true
                  },
                  "derive_shipping": {
                    "description": "Let the USP text be filled from the shop's shipping settings instead of a fixed text.",
                    "type": "boolean",
                    "example": false
                  },
                  "sort_order": {
                    "description": "Position in the list; lower comes first.",
                    "type": "integer",
                    "example": 10
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Free shipping",
                "text": "Vanaf €50 in Nederland",
                "icon": "truck",
                "visible": true,
                "derive_shipping": false,
                "sort_order": 10
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created USP",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "usp": {
                    "uuid": "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                    "title": "Free shipping",
                    "text": "Vanaf €50 in Nederland",
                    "icon": "truck",
                    "visible": true,
                    "derive_shipping": false,
                    "sort_order": 10
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:write\" scope, which this app was not granted.",
                    "scope": "usp:write"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" is required.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "**Required scope:** `webhooks:read` — Read this app's webhook subscriptions and the available topics",
        "operationId": "webhooksList",
        "responses": {
          "200": {
            "description": "This app's webhook subscriptions and every available topic",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "subscriptions": [
                    {
                      "topic": "product.updated",
                      "address": "https://bike-stock-sync.example/hooks/sitepack"
                    }
                  ],
                  "available_topics": [
                    "content.created",
                    "content.updated",
                    "product.updated",
                    "order.created"
                  ],
                  "managed_in": "app.json"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"webhooks:read\" scope, which this app was not granted.",
                    "scope": "webhooks:read"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "webhooks:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/navigations/items/{uuid}": {
      "delete": {
        "tags": [
          "Navigation"
        ],
        "summary": "Delete a navigation item",
        "description": "**Required scope:** `navigation:write` — Change navigations and their items",
        "operationId": "navigationDeleteItem",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the navigation item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The navigation item was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:write\" scope, which this app was not granted.",
                    "scope": "navigation:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That navigation item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That navigation item does not exist on this site.",
                    "resource": "navigation item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Navigation"
        ],
        "summary": "Update a navigation item",
        "description": "**Required scope:** `navigation:write` — Change navigations and their items",
        "operationId": "navigationUpdateItem",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the navigation item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "label": {
                    "description": "The text shown in the menu.",
                    "type": "string",
                    "example": "About us"
                  },
                  "url": {
                    "description": "Where the item points.",
                    "type": "string",
                    "example": "/about-us"
                  },
                  "sort_order": {
                    "description": "Position within the menu; lower comes first.",
                    "type": "integer",
                    "example": 30
                  },
                  "parent_uuid": {
                    "description": "Nest this item under another item. Send `null` to move it back to the top level.",
                    "type": "string",
                    "example": "0192c5a4-6d29-7c85-8e13-52af7b904d61"
                  },
                  "display_as_button": {
                    "description": "Render the item as a button.",
                    "type": "boolean",
                    "example": true
                  }
                },
                "type": "object"
              },
              "example": {
                "label": "About us",
                "url": "/about-us",
                "sort_order": 30,
                "parent_uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                "display_as_button": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated navigation item",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "item": {
                    "uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                    "label": "About us",
                    "url": "/about-us",
                    "sort_order": 30,
                    "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "category_uuid": null,
                    "display_as_button": false
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:write\" scope, which this app was not granted.",
                    "scope": "navigation:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That navigation item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That navigation item does not exist on this site.",
                    "resource": "navigation item"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"label\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"label\" cannot be empty.",
                    "field": "label"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/variants/{variantUuid}/stock": {
      "patch": {
        "tags": [
          "Products"
        ],
        "summary": "Set variant stock",
        "description": "Set a single variant's stock to an absolute value. Behind the inventory scope, the\nsame as product-level stock: managing the catalogue must not imply moving stock.\n\n**Required scope:** `inventory:write` — Adjust stock and manage stock locations",
        "operationId": "productsVariantStock",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "variantUuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "stock"
                ],
                "properties": {
                  "stock": {
                    "description": "The variant's new stock level, as an absolute value. Clamped at zero. Per-location stock is managed through the full variant upsert; this sets the flat level a plain variant uses.",
                    "type": "integer",
                    "example": 12
                  }
                },
                "type": "object"
              },
              "example": {
                "stock": 12
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The variant with its new stock level",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "variant": {
                    "uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                    "sku": "RF-L-GE",
                    "ean": "8710000000001",
                    "price_cents": 4495,
                    "stock": 6,
                    "available": true,
                    "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                    "position": 0,
                    "option_values": {
                      "Size": "L",
                      "Colour": "Yellow"
                    },
                    "properties": [
                      {
                        "name": "Materiaal",
                        "value": "Katoen"
                      }
                    ],
                    "metafields": [
                      {
                        "name": "GTIN",
                        "type": "text",
                        "value": "8710000000001"
                      }
                    ],
                    "stock_locations": []
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"inventory:write\" scope, which this app was not granted.",
                    "scope": "inventory:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"stock\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"stock\" is required.",
                    "field": "stock"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "inventory:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/variants/{variantUuid}/metafields": {
      "put": {
        "tags": [
          "Products"
        ],
        "summary": "Replace variant metafields",
        "description": "Replace a single variant's metafields. The full set: metafields not included are\ndropped. Stock, properties and facets are left untouched.\n\n**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsVariantMetafields",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "variantUuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "metafields"
                ],
                "properties": {
                  "metafields": {
                    "description": "The full set of metafields for the variant, each `{name, type, value}`. Metafields that are not included are dropped. Stock, properties and facets are left untouched.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "name": "HS code",
                        "type": "text",
                        "value": "6109"
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "metafields": [
                  {
                    "name": "HS code",
                    "type": "text",
                    "value": "6109"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The variant with its stored metafields",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "variant": {
                    "uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                    "sku": "RF-L-GE",
                    "ean": "8710000000001",
                    "price_cents": 4495,
                    "stock": 6,
                    "available": true,
                    "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                    "position": 0,
                    "option_values": {
                      "Size": "L",
                      "Colour": "Yellow"
                    },
                    "properties": [
                      {
                        "name": "Materiaal",
                        "value": "Katoen"
                      }
                    ],
                    "metafields": [
                      {
                        "name": "GTIN",
                        "type": "text",
                        "value": "8710000000001"
                      }
                    ],
                    "stock_locations": []
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"metafields\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"metafields\" is required.",
                    "field": "metafields"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/blogs/{uuid}/publish": {
      "post": {
        "tags": [
          "Blogs"
        ],
        "summary": "Publish a blog post",
        "description": "**Required scope:** `blogs:write` — Create, change, publish and delete blog posts",
        "operationId": "blogsPublish",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the blog post. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The published blog post",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "blog": {
                    "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                    "title": "Winter tyres: what to look for",
                    "slug": "winter-tyres-what-to-look-for",
                    "status": "published",
                    "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                    "tags": [
                      "maintenance",
                      "winter"
                    ],
                    "created": "2026-02-11T08:00:00+00:00",
                    "modified": "2026-02-13T16:44:02+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:write\" scope, which this app was not granted.",
                    "scope": "blogs:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That blog post does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That blog post does not exist on this site.",
                    "resource": "blog post"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/blogs/{uuid}/unpublish": {
      "post": {
        "tags": [
          "Blogs"
        ],
        "summary": "Unpublish a blog post",
        "description": "**Required scope:** `blogs:write` — Create, change, publish and delete blog posts",
        "operationId": "blogsUnpublish",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the blog post. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The unpublished blog post",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "blog": {
                    "status": "draft",
                    "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                    "title": "Winter tyres: what to look for",
                    "slug": "winter-tyres-what-to-look-for",
                    "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                    "tags": [
                      "maintenance",
                      "winter"
                    ],
                    "created": "2026-02-11T08:00:00+00:00",
                    "modified": "2026-02-13T16:44:02+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:write\" scope, which this app was not granted.",
                    "scope": "blogs:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That blog post does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That blog post does not exist on this site.",
                    "resource": "blog post"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/collections/{uuid}/rules": {
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "List collection rules",
        "description": "Read the rules that decide what a collection contains.\n\n**Required scope:** `collections:read` — Read collections and their rules",
        "operationId": "collectionsRules",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the collection. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rules of a collection",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "rules": [
                    {
                      "uuid": "0192c51c-9f2d-7e58-8b04-71a3c6d90f25",
                      "column": "brand",
                      "value": "Wheelhouse",
                      "exact": true
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:read\" scope, which this app was not granted.",
                    "scope": "collections:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That collection does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That collection does not exist on this site.",
                    "resource": "collection"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Collections"
        ],
        "summary": "Replace collection rules",
        "description": "Replace the rules of a collection.\n\nAll of them at once: a collection with half its rules applied selects the wrong\nproducts, which is worse than a refused request.\n\n**Required scope:** `collections:write` — Create, change and delete collections",
        "operationId": "collectionsReplaceRules",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the collection. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "rules"
                ],
                "properties": {
                  "rules": {
                    "description": "The whole rule set. Each rule needs a `column` (the product field to match on) and a `value`; `exact` decides whether the value has to match in full or only be contained, and defaults to true.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "column": "brand",
                        "value": "Wheelhouse",
                        "exact": true
                      },
                      {
                        "column": "name",
                        "value": "Aero",
                        "exact": false
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "rules": [
                  {
                    "column": "brand",
                    "value": "Wheelhouse",
                    "exact": true
                  },
                  {
                    "column": "name",
                    "value": "Aero",
                    "exact": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored rules",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "rules": [
                    {
                      "uuid": "0192c51c-9f2d-7e58-8b04-71a3c6d90f25",
                      "column": "brand",
                      "value": "Wheelhouse",
                      "exact": true
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:write\" scope, which this app was not granted.",
                    "scope": "collections:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That collection does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That collection does not exist on this site.",
                    "resource": "collection"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"rules\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"rules\" is required.",
                    "field": "rules"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}/publish": {
      "post": {
        "tags": [
          "Content"
        ],
        "summary": "Publish a page",
        "description": "**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentPublish",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The published page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "status": "published",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}/unpublish": {
      "post": {
        "tags": [
          "Content"
        ],
        "summary": "Unpublish a page",
        "description": "**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentUnpublish",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The unpublished page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "status": "draft",
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}/revisions": {
      "get": {
        "tags": [
          "Content"
        ],
        "summary": "List page revisions",
        "description": "The stored revisions of a page: what it looked like before each save.\n\n**Required scope:** `content:read` — Read pages, their elements and revisions",
        "operationId": "contentRevisions",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The revisions of a page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "revisions": [
                    {
                      "uuid": "0192c4a2-1b70-7f6e-84a1-2d0c5e9b7a13",
                      "created": "2026-05-19T14:02:07+00:00"
                    },
                    {
                      "uuid": "0192c4a2-0c31-7b95-8f27-13d6e0a48c52",
                      "created": "2026-04-02T08:41:19+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:read\" scope, which this app was not granted.",
                    "scope": "content:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}/revisions/{revisionUuid}/restore": {
      "post": {
        "tags": [
          "Content"
        ],
        "summary": "Restore a page revision",
        "description": "Roll a page back to one of its revisions.\n\nThe rollback is itself a save, so the replaced state becomes the newest revision -\nundoing a rollback is another rollback, not a lost version.\n\n**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentRestoreRevision",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "revisionUuid",
            "in": "path",
            "description": "The uuid of the revision to restore, from `GET /content/{uuid}/revisions`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a2-1b70-7f6e-84a1-2d0c5e9b7a13"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The restored page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "status": "published",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00",
                    "seo": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "open_graph_description": "Building bikes since 1978.",
                      "focus_keyword": "bike shop london",
                      "no_index": false,
                      "score": 78
                    },
                    "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                    "template_key": null,
                    "editor_version": 1,
                    "elements": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}/elements": {
      "get": {
        "tags": [
          "Content"
        ],
        "summary": "Get page elements",
        "description": "**Required scope:** `content:read` — Read pages, their elements and revisions",
        "operationId": "contentElements",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The element tree of a page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                  "elements": [
                    {
                      "type": "pageHeading",
                      "value": "About us",
                      "settings": {
                        "align": "center"
                      }
                    },
                    {
                      "type": "text",
                      "value": "<p>We have been building bikes since 1978.</p>",
                      "settings": {
                        "align": "left",
                        "textColor": "#202223"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:read\" scope, which this app was not granted.",
                    "scope": "content:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Content"
        ],
        "summary": "Replace page elements",
        "description": "Replace a page's element tree. Inline `style=` attributes are stripped and their\nalignment/colour lifted into element settings, the same rule the editor and the\npage generator follow.\n\n**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentReplaceElements",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "elements"
                ],
                "properties": {
                  "elements": {
                    "description": "The whole element tree, in render order. Sending `[]` empties the page. Inline `style=` attributes are stripped and their alignment and colour lifted into `settings`; scripting is removed.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "elements": [
                  {
                    "type": "pageHeading",
                    "value": "About us",
                    "settings": {
                      "align": "center"
                    }
                  },
                  {
                    "type": "text",
                    "value": "<p>We have been building bikes since 1978.</p>",
                    "settings": {
                      "align": "left",
                      "textColor": "#202223"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored element tree",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                  "elements": [
                    {
                      "type": "pageHeading",
                      "value": "About us",
                      "settings": {
                        "align": "center"
                      }
                    },
                    {
                      "type": "text",
                      "value": "<p>We have been building bikes since 1978.</p>",
                      "settings": {
                        "align": "left",
                        "textColor": "#202223"
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"elements\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"elements\" is required.",
                    "field": "elements"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/{uuid}/dns": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "List DNS records",
        "description": "**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "externalDocs": {
          "description": "DNS management",
          "url": "https://help.sitepack.eu/docs/domains/dns-management"
        },
        "operationId": "domainsDns",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the domain. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The DNS records of a domain",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "records": {
                    "records": [
                      {
                        "type": "A",
                        "name": "@",
                        "value": "185.31.17.20",
                        "priority": null,
                        "locked": true
                      },
                      {
                        "type": "MX",
                        "name": "@",
                        "value": "mx1.qboxmail.com",
                        "priority": 10,
                        "locked": false
                      }
                    ],
                    "nameservers": [
                      "ns1.sitepack.io",
                      "ns2.sitepack.io"
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That domain does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That domain does not exist on this site.",
                    "resource": "domain"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Domains"
        ],
        "summary": "Replace DNS records",
        "description": "Replace the DNS records of a domain.\n\nThe whole zone at once: a zone with half a change applied can point a live domain\nat nothing, and DNS mistakes take a TTL to undo. The change is applied at the DNS\nprovider straight away and the zone comes back as it is afterwards.\n\n**Required scope:** `domains:write` — Change DNS records and domain holders",
        "externalDocs": {
          "description": "DNS management",
          "url": "https://help.sitepack.eu/docs/domains/dns-management"
        },
        "operationId": "domainsReplaceDns",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the domain. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "records"
                ],
                "properties": {
                  "records": {
                    "description": "The whole zone. Each record needs `type`, `name` and `value`; `priority` is for MX and SRV. An empty array is refused - it would take the domain offline.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "A",
                        "name": "@",
                        "value": "185.31.17.20",
                        "priority": null,
                        "locked": true
                      },
                      {
                        "type": "MX",
                        "name": "@",
                        "value": "mx1.qboxmail.com",
                        "priority": 10,
                        "locked": false
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "records": [
                  {
                    "type": "A",
                    "name": "@",
                    "value": "185.31.17.20",
                    "priority": null,
                    "locked": true
                  },
                  {
                    "type": "MX",
                    "name": "@",
                    "value": "mx1.qboxmail.com",
                    "priority": 10,
                    "locked": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The zone after the change",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "domain": "wheelhouse.nl",
                  "records": [
                    {
                      "type": "A",
                      "name": "@",
                      "value": "185.31.17.20",
                      "priority": null,
                      "locked": true
                    },
                    {
                      "type": "MX",
                      "name": "@",
                      "value": "mx1.qboxmail.com",
                      "priority": 10,
                      "locked": false
                    }
                  ],
                  "changes": {
                    "zone": "wheelhouse-nl",
                    "added": [
                      "MX wheelhouse.nl. 10 mx1.qboxmail.com. ttl 3600"
                    ],
                    "removed": [],
                    "summary": "1 record(s) added, 0 removed"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:write\" scope, which this app was not granted.",
                    "scope": "domains:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That domain does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That domain does not exist on this site.",
                    "resource": "domain"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"records\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"records\" is required.",
                    "field": "records"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/mail/accounts/{uuid}/quota": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "Read mailbox usage",
        "description": "**Required scope:** `mail:read` — Read mailboxes and mailbox slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailQuota",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the mailbox. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mailbox quota, when the provider reports one",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "quota": {
                    "max_quota": 5368709120,
                    "used_quota": 851443712,
                    "used_quota_human": "812 MB",
                    "messages": 4301
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:read\" scope, which this app was not granted.",
                    "scope": "mail:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That mailbox does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That mailbox does not exist on this site.",
                    "resource": "mailbox"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/mail/accounts/{uuid}/change-password": {
      "post": {
        "tags": [
          "Mail"
        ],
        "summary": "Change a mailbox password",
        "description": "**Required scope:** `mail:write` — Create, change and delete mailboxes within the free slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailChangePassword",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the mailbox. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The new one-time password",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "password": "b70e14c9a5d2f831"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:write\" scope, which this app was not granted.",
                    "scope": "mail:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That mailbox does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That mailbox does not exist on this site.",
                    "resource": "mailbox"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/modules/{key}/settings": {
      "get": {
        "tags": [
          "Modules"
        ],
        "summary": "Read module settings",
        "description": "Read one module's settings.\n\n**Required scope:** `modules:read` — Read the site modules and their settings",
        "operationId": "modulesSettings",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "The module key, as returned by `GET /modules`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ContactForm"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The settings of one module",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "module": "ContactForm",
                  "settings": {
                    "recipient": "info@wheelhouse.nl",
                    "send_confirmation": "1"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"modules:read\" scope, which this app was not granted.",
                    "scope": "modules:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That module does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That module does not exist on this site.",
                    "resource": "module"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "modules:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Modules"
        ],
        "summary": "Update module settings",
        "description": "Change a module's settings.\n\nA merge, not a replace: these settings belong to the merchant and are shared with\nthe admin screens, so an app that knows about two settings must not wipe a third\nit has never heard of.\n\n**Required scope:** `modules:write` — Change module settings",
        "operationId": "modulesUpdateSettings",
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "The module key, as returned by `GET /modules`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "ContactForm"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "settings"
                ],
                "properties": {
                  "settings": {
                    "description": "The settings to write, as a flat name/value map. A merge, not a replace: a setting left out keeps its value, so an app cannot wipe a setting it has never heard of. At least one setting is required.",
                    "type": "object",
                    "example": {
                      "recipient": "sales@wheelhouse.nl"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "settings": {
                  "recipient": "sales@wheelhouse.nl"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored settings",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "module": "ContactForm",
                  "settings": {
                    "recipient": "sales@wheelhouse.nl",
                    "send_confirmation": "1"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"modules:write\" scope, which this app was not granted.",
                    "scope": "modules:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That module does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That module does not exist on this site.",
                    "resource": "module"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"settings\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"settings\" is required.",
                    "field": "settings"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "modules:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/navigations/{section}/items": {
      "get": {
        "tags": [
          "Navigation"
        ],
        "summary": "List navigation items",
        "description": "**Required scope:** `navigation:read` — Read navigations and their items",
        "operationId": "navigationItems",
        "parameters": [
          {
            "name": "section",
            "in": "path",
            "description": "The navigation section, as returned by `GET /navigations`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "main"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The items of one navigation, in order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "section": "main",
                  "items": [
                    {
                      "uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                      "label": "About us",
                      "url": "/about-us",
                      "sort_order": 30,
                      "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                      "category_uuid": null,
                      "display_as_button": false
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:read\" scope, which this app was not granted.",
                    "scope": "navigation:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That navigation item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That navigation item does not exist on this site.",
                    "resource": "navigation item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Navigation"
        ],
        "summary": "Reorder a navigation",
        "description": "Reorder a whole navigation in one call: the \"items\" array of uuids is the new\norder. Nothing is written unless every uuid resolves, so a typo cannot leave a\nmenu half reordered.\n\n**Required scope:** `navigation:write` — Change navigations and their items",
        "operationId": "navigationReorder",
        "parameters": [
          {
            "name": "section",
            "in": "path",
            "description": "The navigation section, as returned by `GET /navigations`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "main"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "items"
                ],
                "properties": {
                  "items": {
                    "description": "The item uuids in their new order. Every item of the menu has to be listed; nothing is written unless all of them resolve.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                      "0192c5a4-71b3-7e40-92cd-08f6b41a7d59"
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "items": [
                  "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                  "0192c5a4-71b3-7e40-92cd-08f6b41a7d59"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The reordered navigation",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "section": "main",
                  "items": [
                    {
                      "uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                      "label": "About us",
                      "url": "/about-us",
                      "sort_order": 30,
                      "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                      "category_uuid": null,
                      "display_as_button": false
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:write\" scope, which this app was not granted.",
                    "scope": "navigation:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That navigation item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That navigation item does not exist on this site.",
                    "resource": "navigation item"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"items\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"items\" is required.",
                    "field": "items"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:write"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Navigation"
        ],
        "summary": "Add a navigation item",
        "description": "**Required scope:** `navigation:write` — Change navigations and their items",
        "operationId": "navigationAddItem",
        "parameters": [
          {
            "name": "section",
            "in": "path",
            "description": "The navigation section, as returned by `GET /navigations`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "main"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "label",
                  "url"
                ],
                "properties": {
                  "label": {
                    "description": "The text shown in the menu.",
                    "type": "string",
                    "example": "About us"
                  },
                  "url": {
                    "description": "Where the item points. A site-relative path or an absolute URL.",
                    "type": "string",
                    "example": "/about-us"
                  },
                  "sort_order": {
                    "description": "Position within the menu; lower comes first. Defaults to 20.",
                    "type": "integer",
                    "example": 30
                  },
                  "content_uuid": {
                    "description": "The page this item links to, when it links to one. Kept so the item follows the page if its slug changes.",
                    "type": "string",
                    "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
                  },
                  "category_uuid": {
                    "description": "The category this item links to, when it links to one.",
                    "type": "string",
                    "example": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938"
                  },
                  "parent_uuid": {
                    "description": "Nest this item under another item of the same menu.",
                    "type": "string",
                    "example": "0192c5a4-6d29-7c85-8e13-52af7b904d61"
                  },
                  "display_as_button": {
                    "description": "Render the item as a button rather than a link. Defaults to false.",
                    "type": "boolean",
                    "example": false
                  }
                },
                "type": "object"
              },
              "example": {
                "label": "About us",
                "url": "/about-us",
                "sort_order": 30,
                "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                "category_uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                "parent_uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                "display_as_button": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The added navigation item",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "item": {
                    "uuid": "0192c5a4-6d29-7c85-8e13-52af7b904d61",
                    "label": "About us",
                    "url": "/about-us",
                    "sort_order": 30,
                    "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "category_uuid": null,
                    "display_as_button": false
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"navigation:write\" scope, which this app was not granted.",
                    "scope": "navigation:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That navigation item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That navigation item does not exist on this site.",
                    "resource": "navigation item"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"label\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"label\" is required.",
                    "field": "label"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "navigation:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/orders/{uuid}/payments": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "List order payments",
        "description": "The payments recorded against an order.\n\n`orders_payments` records what was attempted at which gateway, not a status\nvocabulary: `status` is a boolean saying whether the attempt succeeded, and the\nmethod is the gateway and the type chosen there (\"mollie\" / \"ideal\"). Reported as\nthey are stored rather than flattened into an invented status string.\n\n**Required scope:** `orders:read` — Read webshop orders, their items and payments",
        "operationId": "ordersPayments",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the order. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payments recorded against an order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "payments": [
                    {
                      "amount_cents": 129900,
                      "paid": true,
                      "gateway": "mollie",
                      "method": "ideal",
                      "reference": "tr_7Hq4kPz9Xa",
                      "created": "2026-06-11T09:34:52+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"orders:read\" scope, which this app was not granted.",
                    "scope": "orders:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That order does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That order does not exist on this site.",
                    "resource": "order"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "orders:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/stock": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "Read product stock",
        "description": "**Required scope:** `inventory:read` — Read stock levels and stock locations",
        "operationId": "productsStock",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current stock and the latest stock changes",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                  "stock": 7,
                  "transactions": [
                    {
                      "uuid": "0192c5d7-2b48-7e60-85f1-9c34a0d726be",
                      "delta": -1,
                      "from": 8,
                      "to": 7,
                      "source": "order",
                      "variant_uuid": null,
                      "order_uuid": "0192c5d9-4e7a-7b21-9f3c-2d8e6a1b5c47",
                      "created": "2026-06-11T09:31:04+00:00"
                    },
                    {
                      "uuid": "0192c5d7-0a16-7c39-84b2-6f7e15c0d982",
                      "delta": 5,
                      "from": 3,
                      "to": 8,
                      "source": "admin",
                      "variant_uuid": null,
                      "order_uuid": null,
                      "created": "2026-06-02T13:12:47+00:00"
                    }
                  ],
                  "transactions_total": 14
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"inventory:read\" scope, which this app was not granted.",
                    "scope": "inventory:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "inventory:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/stock/transactions": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List stock changes",
        "description": "**Required scope:** `inventory:read` — Read stock levels and stock locations",
        "operationId": "productsStockTransactions",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
            "required": false,
            "schema": {
              "description": "Which page to read, starting at 1. Out-of-range pages come back empty rather than as an error.",
              "type": "integer",
              "default": 1,
              "example": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many records per page. Defaults to 25, clamped to 100.",
            "required": false,
            "schema": {
              "description": "How many records per page. Defaults to 25, clamped to 100.",
              "type": "integer",
              "default": 25,
              "maximum": 100,
              "example": 25
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paginated list of the product's stock changes, newest first",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "items": [
                    {
                      "uuid": "0192c5d7-2b48-7e60-85f1-9c34a0d726be",
                      "delta": -1,
                      "from": 8,
                      "to": 7,
                      "source": "order",
                      "variant_uuid": null,
                      "order_uuid": "0192c5d9-4e7a-7b21-9f3c-2d8e6a1b5c47",
                      "created": "2026-06-11T09:31:04+00:00"
                    }
                  ],
                  "pagination": {
                    "page": 1,
                    "limit": 25,
                    "totalRecords": 14,
                    "totalPages": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"inventory:read\" scope, which this app was not granted.",
                    "scope": "inventory:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "inventory:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/stock/adjust": {
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Adjust product stock",
        "description": "Adjust stock by a delta. A delta rather than an absolute value, so two apps\nadjusting at the same time both land instead of overwriting each other.\n\n**Required scope:** `inventory:write` — Adjust stock and manage stock locations",
        "operationId": "productsAdjustStock",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "delta"
                ],
                "properties": {
                  "delta": {
                    "description": "How much to move stock by. Negative takes stock away; the result is clamped at zero. A delta rather than an absolute level, so two apps adjusting at the same time both land.",
                    "type": "integer",
                    "example": -1
                  }
                },
                "type": "object"
              },
              "example": {
                "delta": -1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The product with its new stock level",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                  "stock": 7
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"inventory:write\" scope, which this app was not granted.",
                    "scope": "inventory:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"delta\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"delta\" is required.",
                    "field": "delta"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "inventory:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/images": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List product images",
        "description": "The images attached to a product, in display order.\n\n**Required scope:** `products:read` — Read products, variants, images and bundles",
        "operationId": "productsImages",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The images of a product",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "images": [
                    {
                      "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "url": "https://cdn.sitepack.io/wheelhouse/road-bike-blue.jpg",
                      "alt_text": "Blue road bike against a white wall",
                      "sort_order": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:read\" scope, which this app was not granted.",
                    "scope": "products:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Products"
        ],
        "summary": "Replace product images",
        "description": "Replace a product's images, in the given order.\n\nTakes media uuids: an image has to exist in the library already, which is what\nkeeps a product from pointing at a file nobody can manage or delete.\n\n**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsReplaceImages",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "media"
                ],
                "properties": {
                  "media": {
                    "description": "The media uuids to attach, in display order. The first is the main image. Every uuid has to exist in the library already and may appear once; sending `[]` detaches all of them.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "0192c4c5-52e0-7a37-b18d-4f902c65a3e1"
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "media": [
                  "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                  "0192c4c5-52e0-7a37-b18d-4f902c65a3e1"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored images",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "images": [
                    {
                      "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "url": "https://cdn.sitepack.io/wheelhouse/road-bike-blue.jpg",
                      "alt_text": "Blue road bike against a white wall",
                      "sort_order": 1
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"media\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"media\" is required.",
                    "field": "media"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/options": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List product options",
        "description": "**Required scope:** `products:read` — Read products, variants, images and bundles",
        "operationId": "productsOptions",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product options (attributes and their values)",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "options": [
                    {
                      "uuid": "0192c5f9-7a02-7b61-8c4d-93e5a1f2064b",
                      "name": "Size",
                      "position": 0,
                      "values": [
                        "L",
                        "XL"
                      ]
                    },
                    {
                      "uuid": "0192c5f9-8b13-7a04-9e26-71c4a0d5382f",
                      "name": "Colour",
                      "position": 1,
                      "values": [
                        "Yellow",
                        "Red"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:read\" scope, which this app was not granted.",
                    "scope": "products:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Products"
        ],
        "summary": "Replace product options",
        "description": "Replace the full set of options for a product. Options that are not included are\nremoved. Changing options does not touch the variants; upsert those separately.\n\n**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsReplaceOptions",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "options"
                ],
                "properties": {
                  "options": {
                    "description": "The full set of options for the product, each `{name, values}`. Options that are not included are removed. Changing options does not touch the variants; upsert those separately.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "name": "Size",
                        "values": [
                          "L",
                          "XL"
                        ]
                      },
                      {
                        "name": "Colour",
                        "values": [
                          "Yellow",
                          "Red"
                        ]
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "options": [
                  {
                    "name": "Size",
                    "values": [
                      "L",
                      "XL"
                    ]
                  },
                  {
                    "name": "Colour",
                    "values": [
                      "Yellow",
                      "Red"
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored product options",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "options": [
                    {
                      "uuid": "0192c5f9-7a02-7b61-8c4d-93e5a1f2064b",
                      "name": "Size",
                      "position": 0,
                      "values": [
                        "L",
                        "XL"
                      ]
                    },
                    {
                      "uuid": "0192c5f9-8b13-7a04-9e26-71c4a0d5382f",
                      "name": "Colour",
                      "position": 1,
                      "values": [
                        "Yellow",
                        "Red"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"options\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"options\" is required.",
                    "field": "options"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/variants": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "List product variants",
        "description": "**Required scope:** `products:read` — Read products, variants, images and bundles",
        "operationId": "productsVariants",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product variants (purchasable option combinations)",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "variants": [
                    {
                      "uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                      "sku": "RF-L-GE",
                      "ean": "8710000000001",
                      "price_cents": 4495,
                      "stock": 6,
                      "available": true,
                      "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "position": 0,
                      "option_values": {
                        "Size": "L",
                        "Colour": "Yellow"
                      },
                      "properties": [
                        {
                          "name": "Materiaal",
                          "value": "Katoen"
                        }
                      ],
                      "metafields": [
                        {
                          "name": "GTIN",
                          "type": "text",
                          "value": "8710000000001"
                        }
                      ],
                      "stock_locations": []
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:read\" scope, which this app was not granted.",
                    "scope": "products:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Upsert product variants",
        "description": "Upsert the full set of variants for a product. The body is the complete set;\nvariants that are not included are removed. A variant carrying a `uuid` is updated\nin place, one without is created.\n\n**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsUpsertVariants",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "variants"
                ],
                "properties": {
                  "variants": {
                    "description": "The full set of variants for the product; variants that are not included are removed. Pass a variant's `uuid` to update it in place, or omit it to create a new one. `optionValues` maps each option name to the chosen value.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "sku": "RF-L-GE",
                        "ean": "8710000000001",
                        "priceCents": 4495,
                        "stock": 6,
                        "mediaUuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                        "optionValues": {
                          "Size": "L",
                          "Colour": "Yellow"
                        },
                        "properties": [
                          {
                            "name": "Materiaal",
                            "value": "Katoen"
                          }
                        ],
                        "metafields": [
                          {
                            "name": "GTIN",
                            "type": "text",
                            "value": "8710000000001"
                          }
                        ]
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "variants": [
                  {
                    "sku": "RF-L-GE",
                    "ean": "8710000000001",
                    "priceCents": 4495,
                    "stock": 6,
                    "mediaUuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                    "optionValues": {
                      "Size": "L",
                      "Colour": "Yellow"
                    },
                    "properties": [
                      {
                        "name": "Materiaal",
                        "value": "Katoen"
                      }
                    ],
                    "metafields": [
                      {
                        "name": "GTIN",
                        "type": "text",
                        "value": "8710000000001"
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored product variants",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "variants": [
                    {
                      "uuid": "0192c60a-3b18-7e29-9d75-05af7c831e42",
                      "sku": "RF-L-GE",
                      "ean": "8710000000001",
                      "price_cents": 4495,
                      "stock": 6,
                      "available": true,
                      "media_uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                      "position": 0,
                      "option_values": {
                        "Size": "L",
                        "Colour": "Yellow"
                      },
                      "properties": [
                        {
                          "name": "Materiaal",
                          "value": "Katoen"
                        }
                      ],
                      "metafields": [
                        {
                          "name": "GTIN",
                          "type": "text",
                          "value": "8710000000001"
                        }
                      ],
                      "stock_locations": []
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"variants\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"variants\" is required.",
                    "field": "variants"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}/variants/{variantUuid}": {
      "delete": {
        "tags": [
          "Products"
        ],
        "summary": "Delete a product variant",
        "description": "**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsDeleteVariant",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "variantUuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The variant was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/leads/{uuid}/notes": {
      "get": {
        "tags": [
          "Leads"
        ],
        "summary": "List lead notes",
        "description": "The notes on a form submission.\n\n**Required scope:** `leads:read` — Read form submissions and their notes",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteLeadNotes",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the form submission. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The notes on a form submission",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "notes": [
                    {
                      "note": "Called back, wants to book a test ride.",
                      "is_system": true,
                      "created": "2026-06-12T11:04:21+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:read\" scope, which this app was not granted.",
                    "scope": "leads:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That form submission does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That form submission does not exist on this site.",
                    "resource": "form submission"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:read"
            ]
          }
        ]
      },
      "post": {
        "tags": [
          "Leads"
        ],
        "summary": "Add a lead note",
        "description": "Add a note to a form submission.\n\n**Required scope:** `leads:write` — Create, change and delete form submissions",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteAddLeadNote",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the form submission. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "note"
                ],
                "properties": {
                  "note": {
                    "description": "The note text. Notes written through the API are marked as system notes, because they did not come from someone typing in the admin.",
                    "type": "string",
                    "example": "Called back, wants to book a test ride."
                  }
                },
                "type": "object"
              },
              "example": {
                "note": "Called back, wants to book a test ride."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The added note",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "note": {
                    "note": "Called back, wants to book a test ride.",
                    "is_system": true,
                    "created": "2026-06-12T11:04:21+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:write\" scope, which this app was not granted.",
                    "scope": "leads:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That form submission does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That form submission does not exist on this site.",
                    "resource": "form submission"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"note\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"note\" is required.",
                    "field": "note"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/translations/{type}/{uuid}": {
      "get": {
        "tags": [
          "Translations"
        ],
        "summary": "List an item's translations",
        "description": "**Required scope:** `translations:read` — Read content translations and site locales",
        "operationId": "translationsList",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "The translatable content type: `page`, `blog`, `product` or `category`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "page"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the translated item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every translation of one item, keyed by locale",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "type": "page",
                  "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                  "translations": [
                    {
                      "uuid": "0192c5b5-1e73-7f40-95c8-7d604ba2e138",
                      "locale": "de",
                      "content_type": "page",
                      "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                      "slug": "ueber-uns",
                      "name": "Über uns",
                      "custom_title": "Über Wheelhouse | Fahrräder seit 1978",
                      "meta_description": "Wer wir sind und wo Sie uns finden.",
                      "open_graph_description": null,
                      "focus_keyword": null,
                      "strings": {
                        "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                      },
                      "modified": "2026-06-04T10:15:33+00:00"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:read\" scope, which this app was not granted.",
                    "scope": "translations:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/translations/{type}/{uuid}/{locale}": {
      "get": {
        "tags": [
          "Translations"
        ],
        "summary": "Get one locale's translation",
        "description": "What there is to translate for one locale: the source strings, the current\ntranslation and how stale it is.\n\n**Required scope:** `translations:read` — Read content translations and site locales",
        "operationId": "translationsGet",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "The translatable content type: `page`, `blog`, `product` or `category`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "page"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the translated item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "locale",
            "in": "path",
            "description": "A locale that is active on this site. `GET /translations/locales` lists them.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "de"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Source units and the current translation for one locale",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "type": "page",
                  "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                  "locale": "de",
                  "translation": {
                    "fields": [
                      {
                        "path": "field#name",
                        "type": "text",
                        "source": "About us",
                        "value": "Über uns",
                        "html": false,
                        "outdated": false,
                        "field": "name"
                      },
                      {
                        "path": "field#meta_description",
                        "type": "text",
                        "source": "Who we are and where to find us.",
                        "value": null,
                        "html": false,
                        "outdated": false,
                        "field": "meta_description"
                      }
                    ],
                    "body": [
                      {
                        "path": "elements#0.value",
                        "type": "html",
                        "source": "<p>We have been building bikes since 1978.</p>",
                        "value": "<p>Wir bauen seit 1978 Fahrräder.</p>",
                        "html": true,
                        "outdated": true
                      }
                    ],
                    "total": 14,
                    "translated": 12,
                    "outdated": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:read\" scope, which this app was not granted.",
                    "scope": "translations:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:read"
            ]
          }
        ]
      },
      "put": {
        "tags": [
          "Translations"
        ],
        "summary": "Save a translation",
        "description": "**Required scope:** `translations:write` — Create and change content translations",
        "operationId": "translationsSavePut",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "The translatable content type: `page`, `blog`, `product` or `category`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "page"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the translated item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "locale",
            "in": "path",
            "description": "A locale that is active on this site. `GET /translations/locales` lists them.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "de"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "values"
                ],
                "properties": {
                  "values": {
                    "description": "The translated strings, keyed by the `path` of the unit they belong to. `field#…` paths are the item's own fields, `region#…` paths are strings inside its element tree. A path left out keeps its current translation; an empty string clears one.",
                    "type": "object",
                    "example": {
                      "field#name": "Über uns",
                      "field#meta_description": "Wer wir sind und wo Sie uns finden.",
                      "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "values": {
                  "field#name": "Über uns",
                  "field#meta_description": "Wer wir sind und wo Sie uns finden.",
                  "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored translation",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "translation": {
                    "uuid": "0192c5b5-1e73-7f40-95c8-7d604ba2e138",
                    "locale": "de",
                    "content_type": "page",
                    "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "slug": "ueber-uns",
                    "name": "Über uns",
                    "custom_title": "Über Wheelhouse | Fahrräder seit 1978",
                    "meta_description": "Wer wir sind und wo Sie uns finden.",
                    "open_graph_description": null,
                    "focus_keyword": null,
                    "strings": {
                      "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                    },
                    "modified": "2026-06-04T10:15:33+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:write\" scope, which this app was not granted.",
                    "scope": "translations:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The locale \"de\" is not active on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "locale_not_active",
                    "message": "The locale \"de\" is not active on this site."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"values\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"values\" is required.",
                    "field": "values"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:write"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Translations"
        ],
        "summary": "Delete a translation",
        "description": "**Required scope:** `translations:write` — Create and change content translations",
        "operationId": "translationsDelete",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "The translatable content type: `page`, `blog`, `product` or `category`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "page"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the translated item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "locale",
            "in": "path",
            "description": "A locale that is active on this site. `GET /translations/locales` lists them.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "de"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The translation was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:write\" scope, which this app was not granted.",
                    "scope": "translations:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Translations"
        ],
        "summary": "Save a translation",
        "description": "**Required scope:** `translations:write` — Create and change content translations",
        "operationId": "translationsSavePatch",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "description": "The translatable content type: `page`, `blog`, `product` or `category`.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "page"
            }
          },
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the translated item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          },
          {
            "name": "locale",
            "in": "path",
            "description": "A locale that is active on this site. `GET /translations/locales` lists them.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "de"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "values"
                ],
                "properties": {
                  "values": {
                    "description": "The translated strings, keyed by the `path` of the unit they belong to. `field#…` paths are the item's own fields, `region#…` paths are strings inside its element tree. A path left out keeps its current translation; an empty string clears one.",
                    "type": "object",
                    "example": {
                      "field#name": "Über uns",
                      "field#meta_description": "Wer wir sind und wo Sie uns finden.",
                      "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "values": {
                  "field#name": "Über uns",
                  "field#meta_description": "Wer wir sind und wo Sie uns finden.",
                  "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The stored translation",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "translation": {
                    "uuid": "0192c5b5-1e73-7f40-95c8-7d604ba2e138",
                    "locale": "de",
                    "content_type": "page",
                    "content_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "slug": "ueber-uns",
                    "name": "Über uns",
                    "custom_title": "Über Wheelhouse | Fahrräder seit 1978",
                    "meta_description": "Wer wir sind und wo Sie uns finden.",
                    "open_graph_description": null,
                    "focus_keyword": null,
                    "strings": {
                      "elements#0.value": "<p>Wir bauen seit 1978 Fahrräder.</p>"
                    },
                    "modified": "2026-06-04T10:15:33+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"translations:write\" scope, which this app was not granted.",
                    "scope": "translations:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That translated item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That translated item does not exist on this site.",
                    "resource": "translated item"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The locale \"de\" is not active on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "locale_not_active",
                    "message": "The locale \"de\" is not active on this site."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"values\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"values\" is required.",
                    "field": "values"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "translations:write"
            ]
          }
        ]
      }
    },
    "/api/authentication/oauth/authorize": {
      "get": {
        "tags": [
          "OAuth"
        ],
        "summary": "Ask a merchant for consent",
        "description": "OAuth 2.0 Authorization Endpoint\n\nDirects the user (shop owner or partner) to login and grant permission to the application.",
        "operationId": "get_oauth_authorize",
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "description": "The ID of the application (e.g., my-seo).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "response_type",
            "in": "query",
            "description": "The type of response requested. Must be \"code\".",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ],
              "example": "code"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "description": "Space-separated list of scopes (e.g., orders:read products:write).",
            "required": true,
            "schema": {
              "type": "string",
              "example": "orders:read products:write"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "description": "The URI to redirect the user to after approval.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "An opaque value used by the client to maintain state between the request and callback (CSRF protection).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "site_id",
            "in": "query",
            "description": "Optional: Force the context to one specific site.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_access",
            "in": "query",
            "description": "Set to true if a Partner-level token is requested.",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success - returns authorization URL or HTML form",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "message": {
                      "type": "string"
                    },
                    "client_name": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "redirect_uri": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_request"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Ask a merchant for consent",
        "description": "Authorization Endpoint (RFC 6749)\nURL: GET /api/authentication/oauth/authorize",
        "operationId": "post_oauth_authorize",
        "responses": {
          "default": {
            "description": ""
          }
        }
      }
    },
    "/api/authentication/oauth/token": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Exchange a code for a token",
        "description": "OAuth 2.0 Token Endpoint\n\nExchange an authorization code, refresh token, or device code for an access token.",
        "operationId": "post_oauth_token",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "required": [
                  "grant_type",
                  "client_id"
                ],
                "properties": {
                  "grant_type": {
                    "description": "The grant type being used.",
                    "type": "string",
                    "enum": [
                      "authorization_code",
                      "refresh_token",
                      "urn:ietf:params:oauth:grant-type:device_code"
                    ],
                    "example": "authorization_code"
                  },
                  "client_id": {
                    "description": "The ID of the application.",
                    "type": "string"
                  },
                  "client_secret": {
                    "description": "The secret of the application (required for confidential apps).",
                    "type": "string"
                  },
                  "code": {
                    "description": "The authorization code (required if grant_type is authorization_code).",
                    "type": "string"
                  },
                  "refresh_token": {
                    "description": "The refresh token (required if grant_type is refresh_token).",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success - returns access token",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 3600
                    },
                    "scope": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string",
                      "nullable": true
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid grant or credentials",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_grant"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/authentication/oauth/register": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Register an MCP connector client",
        "description": "OAuth 2.0 Dynamic Client Registration (MCP connector)\n\nRegisters a public OAuth client for the SitePack MCP connector.",
        "operationId": "post_oauth_register",
        "responses": {
          "201": {
            "description": "Client registered"
          },
          "400": {
            "description": "Invalid client metadata"
          }
        }
      }
    },
    "/api/authentication/oauth/device/code": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Start device authorization",
        "description": "Device Authorization Endpoint\n\nSpecifically for the sitepack login flow in the terminal, where the user cannot type a URL but must enter a code.",
        "operationId": "post_oauth_device_code",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "client_id"
                ],
                "properties": {
                  "client_id": {
                    "description": "The ID of the CLI (e.g., sitepack-cli).",
                    "type": "string"
                  },
                  "scope": {
                    "description": "Usually theme:write dev:access.",
                    "type": "string",
                    "example": "theme:write dev:access"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success - returns user code and verification URI",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "user_code": {
                      "type": "string",
                      "example": "ABCD-1234"
                    },
                    "verification_uri": {
                      "type": "string",
                      "example": "https://core.sitepack.nl/activate"
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 300
                    },
                    "interval": {
                      "type": "integer",
                      "example": 5
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "invalid_request"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  },
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/authentication/oauth/introspect": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Inspect a token",
        "description": "OAuth 2.0 Introspection Endpoint\n\nUseful for your own microservices or internal checks to see if an X-SitePack-Access-Token is still valid.",
        "operationId": "post_oauth_introspect",
        "responses": {
          "200": {
            "description": "Token status info",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "active": {
                      "type": "boolean",
                      "example": true
                    },
                    "scope": {
                      "type": "string",
                      "example": "orders:read"
                    },
                    "client_id": {
                      "type": "string",
                      "example": "my-seo"
                    },
                    "sub": {
                      "type": "string",
                      "example": "user_123"
                    },
                    "site_id": {
                      "type": "integer",
                      "example": 987,
                      "nullable": true
                    },
                    "partner_access": {
                      "type": "boolean",
                      "example": false
                    },
                    "exp": {
                      "type": "integer",
                      "example": 1710854400,
                      "nullable": true
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - Missing or invalid Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/api/authentication/oauth/revoke": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Revoke a token",
        "description": "OAuth 2.0 Revocation Endpoint\n\nTo break a link directly (e.g., when a user uninstalls the app).",
        "operationId": "post_oauth_revoke",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "description": "The token that needs to be invalidated.",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token revoked",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "revoked"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request"
          }
        }
      }
    },
    "/api/authentication/oauth/whoami": {
      "get": {
        "tags": [
          "OAuth"
        ],
        "summary": "Identify a token",
        "description": "Whoami Action\n\nReturns basic account info for the authenticated user.",
        "operationId": "get_oauth_whoami",
        "responses": {
          "200": {
            "description": "User information",
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "id": {
                      "type": "integer",
                      "example": 1
                    },
                    "email": {
                      "type": "string",
                      "example": "user@example.com"
                    },
                    "first_name": {
                      "type": "string",
                      "example": "John"
                    },
                    "last_name": {
                      "type": "string",
                      "example": "Doe"
                    }
                  },
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          }
        },
        "security": [
          {
            "AccessTokenAuth": []
          }
        ]
      }
    },
    "/api/public/v1/me": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Who am I",
        "description": "Who am I: the app, the site it is installed on and the scopes that are actually\nin force right now (token n installation), not the ones originally requested.\n\n**Required scope:** none. Any valid app token may call this.",
        "operationId": "metaMe",
        "responses": {
          "200": {
            "description": "The calling app, its site and its effective scopes",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "app": {
                    "uuid": "0192c3f0-4a1b-7e29-8d67-1c0b5a94f732",
                    "name": "Bike Stock Sync",
                    "version": 3
                  },
                  "site": {
                    "uuid": "0192c3e1-9d52-7b08-a4f3-6e17c025b8d9",
                    "name": "Wheelhouse",
                    "domain": "wheelhouse.nl"
                  },
                  "installation": {
                    "uuid": "0192c3f1-2b64-7c93-8e50-4a19d7f06c25",
                    "status": "active",
                    "installed_at": "2026-01-08T10:22:31+00:00"
                  },
                  "scopes": [
                    "products:read",
                    "products:write",
                    "inventory:write"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": []
          }
        ]
      }
    },
    "/api/public/v1/scopes": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "List every scope",
        "description": "The full scope catalogue, so an app (or a generated client) can discover every\nscope that exists and which of them it currently holds.\n\n**Required scope:** none. Any valid app token may call this.",
        "operationId": "metaScopes",
        "responses": {
          "200": {
            "description": "Every scope the public API defines",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "scopes": [
                    {
                      "scope": "products:read",
                      "section": "products",
                      "action": "read",
                      "description": "Read products, variants, images and bundles",
                      "implies": [],
                      "granted": true
                    },
                    {
                      "scope": "products:write",
                      "section": "products",
                      "action": "write",
                      "description": "Create, change and delete products and their variants",
                      "implies": [
                        "products:read"
                      ],
                      "granted": true
                    },
                    {
                      "scope": "orders:read",
                      "section": "orders",
                      "action": "read",
                      "description": "Read webshop orders, their items and payments",
                      "implies": [],
                      "granted": false
                    }
                  ],
                  "granted": [
                    "products:read",
                    "products:write",
                    "inventory:write"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": []
          }
        ]
      }
    },
    "/api/public/v1/blogs/{uuid}": {
      "get": {
        "tags": [
          "Blogs"
        ],
        "summary": "Get a blog post",
        "description": "**Required scope:** `blogs:read` — Read blog posts and blog categories",
        "operationId": "blogsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the blog post. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One blog post including its elements",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "blog": {
                    "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                    "title": "Winter tyres: what to look for",
                    "slug": "winter-tyres-what-to-look-for",
                    "status": "published",
                    "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                    "tags": [
                      "maintenance",
                      "winter"
                    ],
                    "created": "2026-02-11T08:00:00+00:00",
                    "modified": "2026-02-13T16:44:02+00:00",
                    "seo": {
                      "custom_title": "Winter tyres for your bike | Wheelhouse",
                      "meta_description": "What to look for when choosing winter tyres.",
                      "focus_keyword": "winter bike tyres"
                    },
                    "elements": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:read\" scope, which this app was not granted.",
                    "scope": "blogs:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That blog post does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That blog post does not exist on this site.",
                    "resource": "blog post"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Blogs"
        ],
        "summary": "Delete a blog post",
        "description": "**Required scope:** `blogs:write` — Create, change, publish and delete blog posts",
        "operationId": "blogsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the blog post. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The blog post was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:write\" scope, which this app was not granted.",
                    "scope": "blogs:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That blog post does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That blog post does not exist on this site.",
                    "resource": "blog post"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Blogs"
        ],
        "summary": "Update a blog post",
        "description": "**Required scope:** `blogs:write` — Create, change, publish and delete blog posts",
        "operationId": "blogsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the blog post. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "title": {
                    "description": "The post title.",
                    "type": "string",
                    "example": "Winter tyres: what to look for"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out.",
                    "type": "string",
                    "example": "winter-tyres-what-to-look-for"
                  },
                  "status": {
                    "description": "Defaults to `draft`. Publishing also indexes the post for site search.",
                    "type": "string",
                    "enum": [
                      "published",
                      "draft"
                    ],
                    "example": "draft"
                  },
                  "tags": {
                    "description": "Tag names. A comma-separated string is accepted too. Replaces the current tags.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      "maintenance",
                      "winter"
                    ]
                  },
                  "main_image": {
                    "description": "The header image URL, also used as the social preview.",
                    "type": "string",
                    "example": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "custom_title": "Winter tyres for your bike | Wheelhouse",
                      "meta_description": "What to look for when choosing winter tyres."
                    }
                  },
                  "elements": {
                    "description": "The element tree of the post, sanitised the same way a page's is.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Winter tyres: what to look for",
                "slug": "winter-tyres-what-to-look-for",
                "status": "draft",
                "tags": [
                  "maintenance",
                  "winter"
                ],
                "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                "seo": {
                  "custom_title": "Winter tyres for your bike | Wheelhouse",
                  "meta_description": "What to look for when choosing winter tyres."
                },
                "elements": [
                  {
                    "type": "pageHeading",
                    "value": "About us",
                    "settings": {
                      "align": "center"
                    }
                  },
                  {
                    "type": "text",
                    "value": "<p>We have been building bikes since 1978.</p>",
                    "settings": {
                      "align": "left",
                      "textColor": "#202223"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated blog post",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "blog": {
                    "uuid": "0192c4b3-77d1-7c02-b8ae-5f3d91c40e77",
                    "title": "Winter tyres: what to look for",
                    "slug": "winter-tyres-what-to-look-for",
                    "status": "published",
                    "main_image": "https://cdn.sitepack.io/wheelhouse/winter-tyres.jpg",
                    "tags": [
                      "maintenance",
                      "winter"
                    ],
                    "created": "2026-02-11T08:00:00+00:00",
                    "modified": "2026-02-13T16:44:02+00:00",
                    "seo": {
                      "custom_title": "Winter tyres for your bike | Wheelhouse",
                      "meta_description": "What to look for when choosing winter tyres.",
                      "focus_keyword": "winter bike tyres"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"blogs:write\" scope, which this app was not granted.",
                    "scope": "blogs:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That blog post does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That blog post does not exist on this site.",
                    "resource": "blog post"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" cannot be empty.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "blogs:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/categories/{uuid}": {
      "get": {
        "tags": [
          "Categories"
        ],
        "summary": "Get a category",
        "description": "**Required scope:** `categories:read` — Read content and product categories",
        "operationId": "categoriesGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the category. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One category",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "category": {
                    "uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "type": "product",
                    "seo": {
                      "custom_title": "Buy road bikes | Wheelhouse",
                      "meta_description": "Our full range of road bikes.",
                      "focus_keyword": "buy road bike"
                    },
                    "created": "2025-11-03T15:22:09+00:00",
                    "modified": "2026-04-18T09:07:44+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"categories:read\" scope, which this app was not granted.",
                    "scope": "categories:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That category does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That category does not exist on this site.",
                    "resource": "category"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "categories:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Categories"
        ],
        "summary": "Delete a category",
        "description": "**Required scope:** `categories:write` — Create, change and delete categories",
        "operationId": "categoriesDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the category. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The category was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"categories:write\" scope, which this app was not granted.",
                    "scope": "categories:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That category does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That category does not exist on this site.",
                    "resource": "category"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "categories:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Categories"
        ],
        "summary": "Update a category",
        "description": "**Required scope:** `categories:write` — Create, change and delete categories",
        "operationId": "categoriesUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the category. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "name": {
                    "description": "The category name.",
                    "type": "string",
                    "example": "Racefietsen"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out.",
                    "type": "string",
                    "example": "road-bikes"
                  },
                  "type": {
                    "description": "What the category groups. Defaults to `blog`.",
                    "type": "string",
                    "enum": [
                      "blog",
                      "page",
                      "product"
                    ],
                    "example": "product"
                  },
                  "parent_uuid": {
                    "description": "Nest under another category. Send `null` to move it to the top level; a category cannot be its own parent.",
                    "type": "string",
                    "example": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "custom_title": "Buy road bikes | Wheelhouse",
                      "meta_description": "Our full range of road bikes."
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefietsen",
                "slug": "road-bikes",
                "type": "product",
                "parent_uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                "seo": {
                  "custom_title": "Buy road bikes | Wheelhouse",
                  "meta_description": "Our full range of road bikes."
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated category",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "category": {
                    "uuid": "0192c4d7-6512-7ab8-91cf-4e2a7b60d938",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "type": "product",
                    "seo": {
                      "custom_title": "Buy road bikes | Wheelhouse",
                      "meta_description": "Our full range of road bikes.",
                      "focus_keyword": "buy road bike"
                    },
                    "created": "2025-11-03T15:22:09+00:00",
                    "modified": "2026-04-18T09:07:44+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"categories:write\" scope, which this app was not granted.",
                    "scope": "categories:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That category does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That category does not exist on this site.",
                    "resource": "category"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" cannot be empty.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "categories:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/collections/{uuid}": {
      "get": {
        "tags": [
          "Collections"
        ],
        "summary": "Get a collection",
        "description": "Read one collection.\n\n**Required scope:** `collections:read` — Read collections and their rules",
        "operationId": "collectionsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the collection. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One collection",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "collection": {
                    "uuid": "0192c51b-4e08-7a92-b13c-6d5f80e24a17",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "created": "2026-02-01T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:read\" scope, which this app was not granted.",
                    "scope": "collections:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That collection does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That collection does not exist on this site.",
                    "resource": "collection"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Collections"
        ],
        "summary": "Delete a collection",
        "description": "Delete a collection and its rules.\n\n**Required scope:** `collections:write` — Create, change and delete collections",
        "operationId": "collectionsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the collection. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The collection was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:write\" scope, which this app was not granted.",
                    "scope": "collections:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That collection does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That collection does not exist on this site.",
                    "resource": "collection"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Collections"
        ],
        "summary": "Update a collection",
        "description": "Change a collection.\n\n**Required scope:** `collections:write` — Create, change and delete collections",
        "operationId": "collectionsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the collection. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "name": {
                    "description": "The collection name.",
                    "type": "string",
                    "example": "Racefietsen"
                  },
                  "slug": {
                    "description": "The URL segment.",
                    "type": "string",
                    "example": "road-bikes"
                  },
                  "rules": {
                    "description": "Replaces every rule of the collection when present. Leave it out to keep them.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "column": "brand",
                        "value": "Wheelhouse",
                        "exact": true
                      },
                      {
                        "column": "name",
                        "value": "Aero",
                        "exact": false
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefietsen",
                "slug": "road-bikes",
                "rules": [
                  {
                    "column": "brand",
                    "value": "Wheelhouse",
                    "exact": true
                  },
                  {
                    "column": "name",
                    "value": "Aero",
                    "exact": false
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated collection",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "collection": {
                    "uuid": "0192c51b-4e08-7a92-b13c-6d5f80e24a17",
                    "name": "Racefietsen",
                    "slug": "road-bikes",
                    "created": "2026-02-01T10:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"collections:write\" scope, which this app was not granted.",
                    "scope": "collections:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That collection does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That collection does not exist on this site.",
                    "resource": "collection"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" cannot be empty.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "collections:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/content/{uuid}": {
      "get": {
        "tags": [
          "Content"
        ],
        "summary": "Get a page",
        "description": "**Required scope:** `content:read` — Read pages, their elements and revisions",
        "operationId": "contentGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page, including its element tree",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "status": "published",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00",
                    "seo": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "open_graph_description": "Building bikes since 1978.",
                      "focus_keyword": "bike shop london",
                      "no_index": false,
                      "score": 78
                    },
                    "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                    "template_key": null,
                    "editor_version": 1,
                    "elements": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:read\" scope, which this app was not granted.",
                    "scope": "content:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Content"
        ],
        "summary": "Delete a page",
        "description": "**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Content"
        ],
        "summary": "Update a page",
        "description": "**Required scope:** `content:write` — Create, change, publish and delete pages",
        "operationId": "contentUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the page. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "title": {
                    "description": "The page title.",
                    "type": "string",
                    "example": "About us"
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the title when left out, and unique per parent page.",
                    "type": "string",
                    "example": "about-us"
                  },
                  "type": {
                    "description": "What kind of page this is. Defaults to `page`.",
                    "type": "string",
                    "enum": [
                      "page",
                      "landing_page",
                      "content"
                    ],
                    "example": "page"
                  },
                  "status": {
                    "description": "Defaults to `draft`, so a page an app is still filling in is not live yet.",
                    "type": "string",
                    "enum": [
                      "published",
                      "draft"
                    ],
                    "example": "draft"
                  },
                  "parent_uuid": {
                    "description": "Nest the page under another page. Send `null` to move it back to the top level.",
                    "type": "string",
                    "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
                  },
                  "is_menu_item": {
                    "description": "Offer the page in the menu picker.",
                    "type": "boolean",
                    "example": true
                  },
                  "template_key": {
                    "description": "A theme template to render with. Setting one makes the page a `landing_page`.",
                    "type": "string",
                    "example": "landing/product-launch"
                  },
                  "image": {
                    "description": "The header image URL. Send `null` to clear it.",
                    "type": "string",
                    "example": "https://cdn.sitepack.io/wheelhouse/about-header.jpg"
                  },
                  "seo": {
                    "description": "Search-engine metadata: `custom_title`, `meta_description`, `open_graph_description`, `focus_keyword` and `no_index`. Only the keys you send are written.",
                    "type": "object",
                    "example": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "no_index": false
                    }
                  },
                  "elements": {
                    "description": "The element tree. Sanitised on the way in: inline `style=` attributes are stripped and scripting removed.",
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "example": [
                      {
                        "type": "pageHeading",
                        "value": "About us",
                        "settings": {
                          "align": "center"
                        }
                      },
                      {
                        "type": "text",
                        "value": "<p>We have been building bikes since 1978.</p>",
                        "settings": {
                          "align": "left",
                          "textColor": "#202223"
                        }
                      }
                    ]
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "About us",
                "slug": "about-us",
                "type": "page",
                "status": "draft",
                "parent_uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                "is_menu_item": true,
                "template_key": "landing/product-launch",
                "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                "seo": {
                  "custom_title": "About Wheelhouse | Bikes since 1978",
                  "meta_description": "Who we are and where to find us.",
                  "no_index": false
                },
                "elements": [
                  {
                    "type": "pageHeading",
                    "value": "About us",
                    "settings": {
                      "align": "center"
                    }
                  },
                  {
                    "type": "text",
                    "value": "<p>We have been building bikes since 1978.</p>",
                    "settings": {
                      "align": "left",
                      "textColor": "#202223"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated page",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "content": {
                    "uuid": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c",
                    "title": "About us",
                    "slug": "about-us",
                    "type": "page",
                    "status": "published",
                    "locale": "en",
                    "is_menu_item": true,
                    "parent_uuid": null,
                    "created": "2026-03-04T09:12:41+00:00",
                    "modified": "2026-05-19T14:02:07+00:00",
                    "seo": {
                      "custom_title": "About Wheelhouse | Bikes since 1978",
                      "meta_description": "Who we are and where to find us.",
                      "open_graph_description": "Building bikes since 1978.",
                      "focus_keyword": "bike shop london",
                      "no_index": false,
                      "score": 78
                    },
                    "image": "https://cdn.sitepack.io/wheelhouse/about-header.jpg",
                    "template_key": null,
                    "editor_version": 1
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"content:write\" scope, which this app was not granted.",
                    "scope": "content:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That page does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That page does not exist on this site.",
                    "resource": "page"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The slug \"about-us\" is already in use.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "slug_exists",
                    "message": "The slug \"about-us\" is already in use."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" cannot be empty.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "content:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/customers/{uuid}": {
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Get a customer",
        "description": "**Required scope:** `customers:read` — Read customers and their addresses",
        "operationId": "customersGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the customer. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One customer",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "customer": {
                    "uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "email": "emma@example.com",
                    "first_name": "Emma",
                    "last_name": "Clarke",
                    "street": "Baker Street",
                    "house_number": "12",
                    "house_number_addition": "A",
                    "postal_code": "NW1 6XE",
                    "city": "London",
                    "country": "GB",
                    "company_name": null,
                    "vat_number": null,
                    "phone_number": "+44 20 7946 0958",
                    "created": "2026-05-02T18:21:07+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"customers:read\" scope, which this app was not granted.",
                    "scope": "customers:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That customer does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That customer does not exist on this site.",
                    "resource": "customer"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "customers:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Customers"
        ],
        "summary": "Update a customer",
        "description": "**Required scope:** `customers:write` — Create and change customers",
        "operationId": "customersUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the customer. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "email": {
                    "description": "The customer's e-mail address. Stored lower-case.",
                    "type": "string",
                    "example": "emma@example.com"
                  },
                  "first_name": {
                    "description": "Given name.",
                    "type": "string",
                    "example": "Emma"
                  },
                  "last_name": {
                    "description": "Family name.",
                    "type": "string",
                    "example": "Clarke"
                  },
                  "street": {
                    "description": "Street name, without the number.",
                    "type": "string",
                    "example": "Baker Street"
                  },
                  "house_number": {
                    "description": "House number.",
                    "type": "string",
                    "example": "12"
                  },
                  "house_number_addition": {
                    "description": "Addition to the house number.",
                    "type": "string",
                    "example": "A"
                  },
                  "postal_code": {
                    "description": "Postal code.",
                    "type": "string",
                    "example": "NW1 6XE"
                  },
                  "city": {
                    "description": "City.",
                    "type": "string",
                    "example": "London"
                  },
                  "country": {
                    "description": "Two-letter country code.",
                    "type": "string",
                    "example": "GB"
                  },
                  "business": {
                    "description": "Mark the customer as a business. Only read when the customer is created.",
                    "type": "boolean",
                    "example": false
                  },
                  "company_name": {
                    "description": "Company name, for business customers.",
                    "type": "string",
                    "example": "Wheelhouse Ltd"
                  },
                  "vat_number": {
                    "description": "VAT number, for business customers.",
                    "type": "string",
                    "example": "NL001234567B01"
                  },
                  "phone_number": {
                    "description": "Phone number.",
                    "type": "string",
                    "example": "+44 20 7946 0958"
                  }
                },
                "type": "object"
              },
              "example": {
                "email": "emma@example.com",
                "first_name": "Emma",
                "last_name": "Clarke",
                "street": "Baker Street",
                "house_number": "12",
                "house_number_addition": "A",
                "postal_code": "NW1 6XE",
                "city": "London",
                "country": "GB",
                "business": false,
                "company_name": "Wheelhouse Ltd",
                "vat_number": "NL001234567B01",
                "phone_number": "+44 20 7946 0958"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated customer",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "customer": {
                    "uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "email": "emma@example.com",
                    "first_name": "Emma",
                    "last_name": "Clarke",
                    "street": "Baker Street",
                    "house_number": "12",
                    "house_number_addition": "A",
                    "postal_code": "NW1 6XE",
                    "city": "London",
                    "country": "GB",
                    "company_name": null,
                    "vat_number": null,
                    "phone_number": "+44 20 7946 0958",
                    "created": "2026-05-02T18:21:07+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"customers:write\" scope, which this app was not granted.",
                    "scope": "customers:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That customer does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That customer does not exist on this site.",
                    "resource": "customer"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"email\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"email\" cannot be empty.",
                    "field": "email"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "customers:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/discount-codes/{uuid}": {
      "get": {
        "tags": [
          "Discounts"
        ],
        "summary": "Get a discount code",
        "description": "**Required scope:** `discounts:read` — Read discount codes",
        "operationId": "discountsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the discount code. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One discount code",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "discount_code": {
                    "uuid": "0192c54e-2c85-7a06-b7d3-49f61e830c92",
                    "code": "LENTE10",
                    "type": "percentage",
                    "value": 10,
                    "status": 1,
                    "redeemable": true,
                    "archived": false,
                    "used_at": null,
                    "created": "2026-03-01T08:00:00+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"discounts:read\" scope, which this app was not granted.",
                    "scope": "discounts:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That discount code does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That discount code does not exist on this site.",
                    "resource": "discount code"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "discounts:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Discounts"
        ],
        "summary": "Archive a discount code",
        "description": "Codes are archived rather than deleted: a code that has been handed to customers\nmust keep existing for the orders that already used it.\n\n**Required scope:** `discounts:write` — Create, change and delete discount codes",
        "operationId": "discountsArchive",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the discount code. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The discount code was archived",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"discounts:write\" scope, which this app was not granted.",
                    "scope": "discounts:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That discount code does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That discount code does not exist on this site.",
                    "resource": "discount code"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "discounts:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/domains/{uuid}": {
      "get": {
        "tags": [
          "Domains"
        ],
        "summary": "Get a domain",
        "description": "**Required scope:** `domains:read` — Read domains, DNS records and domain holders",
        "operationId": "domainsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the domain. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One domain",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "domain": {
                    "uuid": "0192c582-8b47-7d31-a05e-6c29f74b1e83",
                    "domain": "wheelhouse.nl",
                    "status": 10,
                    "protocol": "https",
                    "auto_renewal": true,
                    "provider": "openprovider",
                    "dns_provider": "sitepack",
                    "mail_provider": "qboxmail"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"domains:read\" scope, which this app was not granted.",
                    "scope": "domains:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That domain does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That domain does not exist on this site.",
                    "resource": "domain"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "domains:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/mail/accounts/{uuid}": {
      "get": {
        "tags": [
          "Mail"
        ],
        "summary": "Get a mailbox",
        "description": "**Required scope:** `mail:read` — Read mailboxes and mailbox slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the mailbox. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One mailbox",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "mailbox": {
                    "uuid": "0192c593-4f18-7a62-b9d7-3081ea562c94",
                    "address": "sales@wheelhouse.nl",
                    "status": 20,
                    "provider": "qboxmail",
                    "password_change_supported": true,
                    "created": "2026-04-07T13:55:11+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:read\" scope, which this app was not granted.",
                    "scope": "mail:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That mailbox does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That mailbox does not exist on this site.",
                    "resource": "mailbox"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Mail"
        ],
        "summary": "Delete a mailbox",
        "description": "**Required scope:** `mail:write` — Create, change and delete mailboxes within the free slots",
        "externalDocs": {
          "description": "Mailboxes on a site",
          "url": "https://help.sitepack.eu/docs/admin/settings/mail"
        },
        "operationId": "mailDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the mailbox. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The mailbox was removed, locally and at the provider",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"mail:write\" scope, which this app was not granted.",
                    "scope": "mail:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That mailbox does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That mailbox does not exist on this site.",
                    "resource": "mailbox"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "mail:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/media/{uuid}": {
      "get": {
        "tags": [
          "Media"
        ],
        "summary": "Get a media item",
        "description": "**Required scope:** `media:read` — Read the media library",
        "operationId": "mediaGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the media item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One media item",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "media": {
                    "uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                    "file_name": "road-bike-blue.jpg",
                    "file_type": "image",
                    "mime_type": "image/jpeg",
                    "file_size": 284113,
                    "alt_text": "Blue road bike against a white wall",
                    "url": "https://cdn.sitepack.io/wheelhouse/road-bike-blue.jpg",
                    "created": "2026-01-22T12:40:18+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"media:read\" scope, which this app was not granted.",
                    "scope": "media:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That media item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That media item does not exist on this site.",
                    "resource": "media item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Media"
        ],
        "summary": "Delete a media item",
        "description": "**Required scope:** `media:write` — Upload, change and delete media",
        "operationId": "mediaDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the media item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The media item was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"media:write\" scope, which this app was not granted.",
                    "scope": "media:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That media item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That media item does not exist on this site.",
                    "resource": "media item"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Media"
        ],
        "summary": "Update a media item",
        "description": "**Required scope:** `media:write` — Upload, change and delete media",
        "operationId": "mediaUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the media item. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "file_name": {
                    "description": "The name shown in the library. Renaming does not move the file: `url` stays the same.",
                    "type": "string",
                    "example": "road-bike-blue.jpg"
                  },
                  "alt_text": {
                    "description": "The alt text used wherever the image is rendered. `null` clears it.",
                    "type": "string",
                    "example": "Blue road bike against a white wall"
                  }
                },
                "type": "object"
              },
              "example": {
                "file_name": "road-bike-blue.jpg",
                "alt_text": "Blue road bike against a white wall"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated media item",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "media": {
                    "uuid": "0192c4c5-3a94-7de1-9b26-8c07f5a13d42",
                    "file_name": "road-bike-blue.jpg",
                    "file_type": "image",
                    "mime_type": "image/jpeg",
                    "file_size": 284113,
                    "alt_text": "Blue road bike against a white wall",
                    "url": "https://cdn.sitepack.io/wheelhouse/road-bike-blue.jpg",
                    "created": "2026-01-22T12:40:18+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"media:write\" scope, which this app was not granted.",
                    "scope": "media:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That media item does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That media item does not exist on this site.",
                    "resource": "media item"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"file_name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"file_name\" cannot be empty.",
                    "field": "file_name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "media:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/orders/{uuid}": {
      "get": {
        "tags": [
          "Orders"
        ],
        "summary": "Get an order",
        "description": "**Required scope:** `orders:read` — Read webshop orders, their items and payments",
        "operationId": "ordersGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the order. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One order, including its line items",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "order": {
                    "uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                    "order_number": "WH-2026-01184",
                    "status": 3,
                    "currency": "EUR",
                    "total_amount_cents": 129900,
                    "shipping_costs_cents": 0,
                    "customer_uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "comment": "Please deliver after 5 pm.",
                    "created": "2026-06-11T09:34:12+00:00",
                    "items": [
                      {
                        "product_name": "Racefiets Aero 2026",
                        "product_barcode": "8712345678904",
                        "quantity": 1,
                        "price_cents": 129900,
                        "total_cents": 129900,
                        "variation_sku": null
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"orders:read\" scope, which this app was not granted.",
                    "scope": "orders:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That order does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That order does not exist on this site.",
                    "resource": "order"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "orders:read"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Orders"
        ],
        "summary": "Change an order status",
        "description": "**Required scope:** `orders:write` — Create orders and change their status",
        "operationId": "ordersUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the order. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "description": "The new status: 1 new, 2 on hold, 3 processing, 4 shipping, 5 cancelled, 6 refunded, 10 completed. The same hooks and mails fire as from the admin.",
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3,
                      4,
                      5,
                      6,
                      10
                    ],
                    "example": 4
                  },
                  "comment": {
                    "description": "Replaces the customer comment on the order. `null` clears it.",
                    "type": "string",
                    "example": "Shipped with PostNL."
                  }
                },
                "type": "object"
              },
              "example": {
                "status": 4,
                "comment": "Shipped with PostNL."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated order",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "order": {
                    "status": 4,
                    "uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                    "order_number": "WH-2026-01184",
                    "currency": "EUR",
                    "total_amount_cents": 129900,
                    "shipping_costs_cents": 0,
                    "customer_uuid": "0192c53d-6b40-7f19-92a7-3e81d54c0a6e",
                    "comment": "Please deliver after 5 pm.",
                    "created": "2026-06-11T09:34:12+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"orders:write\" scope, which this app was not granted.",
                    "scope": "orders:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That order does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That order does not exist on this site.",
                    "resource": "order"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"status\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"status\" is required.",
                    "field": "status"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "orders:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/products/{uuid}": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "Get a product",
        "description": "**Required scope:** `products:read` — Read products, variants, images and bundles",
        "operationId": "productsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One product",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "product": {
                    "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                    "name": "Racefiets Aero 2026",
                    "slug": "road-bike-aero-2026",
                    "sku": "RF-2026-BLUE-54",
                    "barcode": "8712345678904",
                    "brand": "Wheelhouse",
                    "model": "Aero",
                    "description": "Light carbon road bike with Shimano 105.",
                    "currency": "EUR",
                    "price_cents": 129900,
                    "price_promo_cents": 119900,
                    "stock": 7,
                    "archived": false,
                    "seo": {
                      "meta_description": "Light carbon road bike with Shimano 105.",
                      "focus_keyword": "carbon road bike"
                    },
                    "created": "2026-01-15T11:02:38+00:00",
                    "modified": "2026-06-11T09:31:04+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:read\" scope, which this app was not granted.",
                    "scope": "products:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Products"
        ],
        "summary": "Delete a product",
        "description": "**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Products"
        ],
        "summary": "Update a product",
        "description": "**Required scope:** `products:write` — Create, change and delete products and their variants",
        "operationId": "productsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the product. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "name": {
                    "description": "The product name.",
                    "type": "string",
                    "example": "Racefiets Aero 2026"
                  },
                  "price_cents": {
                    "description": "The regular price in cents, excluding nothing - it is what the shop shows. Cannot be negative.",
                    "type": "integer",
                    "example": 129900
                  },
                  "slug": {
                    "description": "The URL segment. Derived from the name when left out.",
                    "type": "string",
                    "example": "road-bike-aero-2026"
                  },
                  "sku": {
                    "description": "Your own article number.",
                    "type": "string",
                    "example": "RF-2026-BLUE-54"
                  },
                  "barcode": {
                    "description": "The EAN or UPC.",
                    "type": "string",
                    "example": "8712345678904"
                  },
                  "brand": {
                    "description": "The brand shown on the product page.",
                    "type": "string",
                    "example": "Wheelhouse"
                  },
                  "model": {
                    "description": "The model name.",
                    "type": "string",
                    "example": "Aero"
                  },
                  "description": {
                    "description": "The product description. HTML is allowed.",
                    "type": "string",
                    "example": "Light carbon road bike with Shimano 105."
                  },
                  "price_promo_cents": {
                    "description": "A sale price in cents, shown struck through against `price_cents`. `null` ends the sale.",
                    "type": "integer",
                    "example": 119900
                  },
                  "stock": {
                    "description": "The absolute stock level. Use `POST /products/{uuid}/stock/adjust` for day-to-day movements: a delta does not overwrite what another app just wrote.",
                    "type": "integer",
                    "example": 7
                  },
                  "archived": {
                    "description": "Archived products stay for existing orders but leave the shop.",
                    "type": "boolean",
                    "example": false
                  },
                  "seo": {
                    "description": "Search-engine metadata: `meta_description` and `focus_keyword`.",
                    "type": "object",
                    "example": {
                      "meta_description": "Light carbon road bike with Shimano 105.",
                      "focus_keyword": "carbon road bike"
                    }
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Racefiets Aero 2026",
                "price_cents": 129900,
                "slug": "road-bike-aero-2026",
                "sku": "RF-2026-BLUE-54",
                "barcode": "8712345678904",
                "brand": "Wheelhouse",
                "model": "Aero",
                "description": "Light carbon road bike with Shimano 105.",
                "price_promo_cents": 119900,
                "stock": 7,
                "archived": false,
                "seo": {
                  "meta_description": "Light carbon road bike with Shimano 105.",
                  "focus_keyword": "carbon road bike"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated product",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "product": {
                    "uuid": "0192c50a-8d21-7b45-9e6f-0c73a2915d8b",
                    "name": "Racefiets Aero 2026",
                    "slug": "road-bike-aero-2026",
                    "sku": "RF-2026-BLUE-54",
                    "barcode": "8712345678904",
                    "brand": "Wheelhouse",
                    "model": "Aero",
                    "description": "Light carbon road bike with Shimano 105.",
                    "currency": "EUR",
                    "price_cents": 129900,
                    "price_promo_cents": 119900,
                    "stock": 7,
                    "archived": false,
                    "seo": {
                      "meta_description": "Light carbon road bike with Shimano 105.",
                      "focus_keyword": "carbon road bike"
                    },
                    "created": "2026-01-15T11:02:38+00:00",
                    "modified": "2026-06-11T09:31:04+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"products:write\" scope, which this app was not granted.",
                    "scope": "products:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That product does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That product does not exist on this site.",
                    "resource": "product"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" cannot be empty.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "products:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/redirects/{uuid}": {
      "get": {
        "tags": [
          "Redirects"
        ],
        "summary": "Get a redirect",
        "description": "**Required scope:** `redirects:read` — Read URL redirects",
        "operationId": "redirectsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the redirect. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One redirect",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "redirect": {
                    "uuid": "0192c4f9-59ba-7e37-97d4-1a8c6b230e5f",
                    "from": "/oude-pagina",
                    "to": "/about-us",
                    "type": 301,
                    "hits": 214
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"redirects:read\" scope, which this app was not granted.",
                    "scope": "redirects:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That redirect does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That redirect does not exist on this site.",
                    "resource": "redirect"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "redirects:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Redirects"
        ],
        "summary": "Delete a redirect",
        "description": "**Required scope:** `redirects:write` — Create, change and delete URL redirects",
        "operationId": "redirectsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the redirect. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The redirect was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"redirects:write\" scope, which this app was not granted.",
                    "scope": "redirects:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That redirect does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That redirect does not exist on this site.",
                    "resource": "redirect"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "redirects:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Redirects"
        ],
        "summary": "Update a redirect",
        "description": "**Required scope:** `redirects:write` — Create, change and delete URL redirects",
        "operationId": "redirectsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the redirect. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "from": {
                    "description": "The path to redirect away from.",
                    "type": "string",
                    "example": "/oude-pagina"
                  },
                  "to": {
                    "description": "Where to send the visitor.",
                    "type": "string",
                    "example": "/about-us"
                  },
                  "type": {
                    "description": "The HTTP status.",
                    "type": "integer",
                    "enum": [
                      301,
                      302
                    ],
                    "example": 301
                  }
                },
                "type": "object"
              },
              "example": {
                "from": "/oude-pagina",
                "to": "/about-us",
                "type": 301
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated redirect",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "redirect": {
                    "uuid": "0192c4f9-59ba-7e37-97d4-1a8c6b230e5f",
                    "from": "/oude-pagina",
                    "to": "/about-us",
                    "type": 301,
                    "hits": 214
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"redirects:write\" scope, which this app was not granted.",
                    "scope": "redirects:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That redirect does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That redirect does not exist on this site.",
                    "resource": "redirect"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"from\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"from\" cannot be empty.",
                    "field": "from"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "redirects:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/shipping-labels/{uuid}": {
      "get": {
        "tags": [
          "Shipping"
        ],
        "summary": "Get a shipping label",
        "description": "Read one shipping label, including its track and trace code.\n\n**Required scope:** `shipping:read` — Read shipping labels and their track and trace codes",
        "operationId": "shippingGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the shipping label. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One shipping label",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "shipping_label": {
                    "uuid": "0192c55f-7d13-7b84-9f52-08ea6c491d37",
                    "order_uuid": "0192c52c-1a7f-7d63-84be-25c908f7b361",
                    "provider": "sendcloud",
                    "carrier": "PostNL",
                    "shipping_method": "PostNL Standaard",
                    "track_trace_code": "3SABCD1234567",
                    "created": "2026-06-12T07:14:29+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"shipping:read\" scope, which this app was not granted.",
                    "scope": "shipping:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That shipping label does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That shipping label does not exist on this site.",
                    "resource": "shipping label"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "shipping:read"
            ]
          }
        ]
      }
    },
    "/api/public/v1/leads/{uuid}": {
      "get": {
        "tags": [
          "Leads"
        ],
        "summary": "Get a lead",
        "description": "One lead, including the subject and message the visitor sent. A repeat submission\nfrom the same email while the lead is still open does not create a new lead: it is\nadded to this one as a note (see the notes endpoint) and fires lead.updated.\n\n**Required scope:** `leads:read` — Read form submissions and their notes",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteLead",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the form submission. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One form submission",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "lead": {
                    "uuid": "0192c560-3e91-7c25-86ab-1d47f0b93e58",
                    "type": 1,
                    "status": 1,
                    "source": "contact_form",
                    "subject": "Test ride",
                    "message": "Hi, I would like to test ride the Aero 2026 in size L this Saturday. Is that possible?",
                    "email": "emma@example.com",
                    "first_name": "Emma",
                    "last_name": "Clarke",
                    "company_name": null,
                    "job_title": null,
                    "phone": "+44 20 7946 0958",
                    "address": "12 Baker Street",
                    "zip": "NW1 6XE",
                    "city": "London",
                    "country": "GB",
                    "language": "en",
                    "rating": 2,
                    "value_cents": null,
                    "created": "2026-06-12T10:58:03+00:00",
                    "updated": "2026-06-12T10:58:03+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:read\" scope, which this app was not granted.",
                    "scope": "leads:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That form submission does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That form submission does not exist on this site.",
                    "resource": "form submission"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Leads"
        ],
        "summary": "Delete a lead",
        "description": "**Required scope:** `leads:write` — Create, change and delete form submissions",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteDeleteLead",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the form submission. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The form submission was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:write\" scope, which this app was not granted.",
                    "scope": "leads:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That form submission does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That form submission does not exist on this site.",
                    "resource": "form submission"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Leads"
        ],
        "summary": "Update a lead",
        "description": "**Required scope:** `leads:write` — Create, change and delete form submissions",
        "externalDocs": {
          "description": "Form submissions",
          "url": "https://help.sitepack.eu/docs/admin/leads"
        },
        "operationId": "siteUpdateLead",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the form submission. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "description": "The new status: 1 new, 2 in contact, 3 waiting on the lead, 4 won, 5 lost.",
                    "type": "integer",
                    "enum": [
                      1,
                      2,
                      3,
                      4,
                      5
                    ],
                    "example": 4
                  },
                  "value_cents": {
                    "description": "The deal value in cents, stored only with status 4 (won). Status 5 (lost) clears it.",
                    "type": "integer",
                    "example": 249900
                  }
                },
                "type": "object"
              },
              "example": {
                "status": 4,
                "value_cents": 249900
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated form submission",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "lead": {
                    "status": 4,
                    "value_cents": 249900,
                    "updated": "2026-06-14T09:30:00+00:00",
                    "uuid": "0192c560-3e91-7c25-86ab-1d47f0b93e58",
                    "type": 1,
                    "source": "contact_form",
                    "subject": "Test ride",
                    "message": "Hi, I would like to test ride the Aero 2026 in size L this Saturday. Is that possible?",
                    "email": "emma@example.com",
                    "first_name": "Emma",
                    "last_name": "Clarke",
                    "company_name": null,
                    "job_title": null,
                    "phone": "+44 20 7946 0958",
                    "address": "12 Baker Street",
                    "zip": "NW1 6XE",
                    "city": "London",
                    "country": "GB",
                    "language": "en",
                    "rating": 2,
                    "created": "2026-06-12T10:58:03+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"leads:write\" scope, which this app was not granted.",
                    "scope": "leads:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That form submission does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That form submission does not exist on this site.",
                    "resource": "form submission"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"status\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"status\" is required.",
                    "field": "status"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "leads:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/comments/{uuid}": {
      "delete": {
        "tags": [
          "Comments"
        ],
        "summary": "Delete a comment",
        "description": "**Required scope:** `comments:write` — Moderate and delete comments",
        "operationId": "siteDeleteComment",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the comment. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The comment was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"comments:write\" scope, which this app was not granted.",
                    "scope": "comments:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That comment does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That comment does not exist on this site.",
                    "resource": "comment"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "comments:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Comments"
        ],
        "summary": "Moderate a comment",
        "description": "**Required scope:** `comments:write` — Moderate and delete comments",
        "operationId": "siteModerateComment",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the comment. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "description": "The moderation decision: 0 awaiting moderation, 1 approved, 2 rejected.",
                    "type": "integer",
                    "enum": [
                      0,
                      1,
                      2
                    ],
                    "example": 1
                  }
                },
                "type": "object"
              },
              "example": {
                "status": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The moderated comment",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "comment": {
                    "status": 1,
                    "uuid": "0192c571-5a26-7e70-93cd-4f18b2ea607d",
                    "author_name": "Jeroen",
                    "title": "Great bike",
                    "comment": "Rides beautifully, even on long trips.",
                    "rating": true,
                    "created": "2026-06-09T19:22:41+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"comments:write\" scope, which this app was not granted.",
                    "scope": "comments:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That comment does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That comment does not exist on this site.",
                    "resource": "comment"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"status\" is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"status\" is required.",
                    "field": "status"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "comments:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/tags/{uuid}": {
      "get": {
        "tags": [
          "Tags"
        ],
        "summary": "Get a tag",
        "description": "**Required scope:** `tags:read` — Read tags",
        "operationId": "tagsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the tag. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One tag",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "tag": {
                    "uuid": "0192c4e8-2f43-7c1d-8a05-93b6ed17f204",
                    "slug": "maintenance",
                    "name": "Onderhoud",
                    "description": "Everything about looking after your bike.",
                    "created": "2025-09-14T07:31:56+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"tags:read\" scope, which this app was not granted.",
                    "scope": "tags:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That tag does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That tag does not exist on this site.",
                    "resource": "tag"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "tags:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Tags"
        ],
        "summary": "Delete a tag",
        "description": "**Required scope:** `tags:write` — Create, change and delete tags",
        "operationId": "tagsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the tag. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tag was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"tags:write\" scope, which this app was not granted.",
                    "scope": "tags:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That tag does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That tag does not exist on this site.",
                    "resource": "tag"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "tags:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "Tags"
        ],
        "summary": "Update a tag",
        "description": "**Required scope:** `tags:write` — Create, change and delete tags",
        "operationId": "tagsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the tag. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "name": {
                    "description": "The tag as a reader sees it.",
                    "type": "string",
                    "example": "Onderhoud"
                  },
                  "description": {
                    "description": "Shown on the tag overview page. `null` clears it.",
                    "type": "string",
                    "example": "Everything about looking after your bike."
                  }
                },
                "type": "object"
              },
              "example": {
                "name": "Onderhoud",
                "description": "Everything about looking after your bike."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated tag",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "tag": {
                    "uuid": "0192c4e8-2f43-7c1d-8a05-93b6ed17f204",
                    "slug": "maintenance",
                    "name": "Onderhoud",
                    "description": "Everything about looking after your bike.",
                    "created": "2025-09-14T07:31:56+00:00"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"tags:write\" scope, which this app was not granted.",
                    "scope": "tags:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That tag does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That tag does not exist on this site.",
                    "resource": "tag"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"name\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"name\" cannot be empty.",
                    "field": "name"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "tags:write"
            ]
          }
        ]
      }
    },
    "/api/public/v1/usps/{uuid}": {
      "get": {
        "tags": [
          "USPs"
        ],
        "summary": "Get a USP",
        "description": "**Required scope:** `usp:read` — Read the site USPs (unique selling points)",
        "operationId": "uspGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the usp. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One USP",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "usp": {
                    "uuid": "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                    "title": "Free shipping",
                    "text": "Vanaf €50 in Nederland",
                    "icon": "truck",
                    "visible": true,
                    "derive_shipping": false,
                    "sort_order": 10
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:read\" scope, which this app was not granted.",
                    "scope": "usp:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That usp does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That usp does not exist on this site.",
                    "resource": "usp"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:read"
            ]
          }
        ]
      },
      "delete": {
        "tags": [
          "USPs"
        ],
        "summary": "Delete a USP",
        "description": "**Required scope:** `usp:write` — Create, change, reorder and delete USPs",
        "operationId": "uspDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the usp. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The USP was deleted",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:write\" scope, which this app was not granted.",
                    "scope": "usp:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That usp does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That usp does not exist on this site.",
                    "resource": "usp"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:write"
            ]
          }
        ]
      },
      "patch": {
        "tags": [
          "USPs"
        ],
        "summary": "Update a USP",
        "description": "**Required scope:** `usp:write` — Create, change, reorder and delete USPs",
        "operationId": "uspUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "The uuid of the usp. Uuids are unique per site: one from another site reads as not found.",
            "required": true,
            "schema": {
              "type": "string",
              "example": "0192c4a1-8e2f-7a31-9c5d-6b1f0a2d4e8c"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "title": {
                    "description": "The USP heading.",
                    "type": "string",
                    "example": "Free shipping"
                  },
                  "text": {
                    "description": "The supporting line under the title.",
                    "type": "string",
                    "example": "Vanaf €50 in Nederland"
                  },
                  "icon": {
                    "description": "The icon name the theme renders.",
                    "type": "string",
                    "example": "truck"
                  },
                  "visible": {
                    "description": "Whether the theme shows this USP.",
                    "type": "boolean",
                    "example": true
                  },
                  "derive_shipping": {
                    "description": "Let the USP text be filled from the shop's shipping settings instead of a fixed text.",
                    "type": "boolean",
                    "example": false
                  },
                  "sort_order": {
                    "description": "Position in the list; lower comes first.",
                    "type": "integer",
                    "example": 10
                  }
                },
                "type": "object"
              },
              "example": {
                "title": "Free shipping",
                "text": "Vanaf €50 in Nederland",
                "icon": "truck",
                "visible": true,
                "derive_shipping": false,
                "sort_order": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated USP",
            "headers": {
              "X-SitePack-RateLimit": {
                "description": "Requests allowed per minute for this installation.",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-SitePack-RateLimit-Tokens": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 574
                }
              },
              "X-SitePack-RateLimit-Reset": {
                "description": "Unix timestamp at which the window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000060
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "success",
                  "usp": {
                    "uuid": "0192c5b5-3e18-7a46-9d72-64bc8a015f3a",
                    "title": "Free shipping",
                    "text": "Vanaf €50 in Nederland",
                    "icon": "truck",
                    "visible": true,
                    "derive_shipping": false,
                    "sort_order": 10
                  }
                }
              }
            }
          },
          "401": {
            "description": "No access token, or the token is invalid or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "invalid_token",
                    "message": "The access token is invalid or expired."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The app lacks the required scope, or its installation is no longer active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "scope_required",
                    "message": "This endpoint needs the \"usp:write\" scope, which this app was not granted.",
                    "scope": "usp:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "That usp does not exist on this site.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "not_found",
                    "message": "That usp does not exist on this site.",
                    "resource": "usp"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Field \"title\" cannot be empty.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "validation_failed",
                    "message": "Field \"title\" cannot be empty.",
                    "field": "title"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Retry after the number of seconds in the Retry-After header.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying. Retrying sooner only spends the window.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "status": "error",
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Retry after the number of seconds in the Retry-After header."
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "OAuth2": [
              "usp:write"
            ]
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SitePackToken": {
        "type": "apiKey",
        "name": "X-SitePack-Access-Token",
        "in": "header"
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "SitePack app authorization. Request an authorization code at /api/authentication/oauth/authorize and exchange it at /api/authentication/oauth/token. Send the result in the \"X-SitePack-Access-Token: <token>\" header (the preferred way in); a standard \"Authorization: Bearer <token>\" header is also accepted.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/api/authentication/oauth/authorize",
            "tokenUrl": "/api/authentication/oauth/token",
            "refreshUrl": "/api/authentication/oauth/token",
            "scopes": {
              "content:read": "Read pages, their elements and revisions",
              "content:write": "Create, change, publish and delete pages",
              "blogs:read": "Read blog posts and blog categories",
              "blogs:write": "Create, change, publish and delete blog posts",
              "translations:read": "Read content translations and site locales",
              "translations:write": "Create and change content translations",
              "elements:read": "Read homepage, footer and sidebar elements",
              "elements:write": "Change homepage, footer and sidebar elements",
              "navigation:read": "Read navigations and their items",
              "navigation:write": "Change navigations and their items",
              "redirects:read": "Read URL redirects",
              "redirects:write": "Create, change and delete URL redirects",
              "media:read": "Read the media library",
              "media:write": "Upload, change and delete media",
              "categories:read": "Read content and product categories",
              "categories:write": "Create, change and delete categories",
              "tags:read": "Read tags",
              "tags:write": "Create, change and delete tags",
              "usp:read": "Read the site USPs (unique selling points)",
              "usp:write": "Create, change, reorder and delete USPs",
              "posts:read": "Read the post types installed apps add to the site, and their entries",
              "posts:write": "Create, change and delete entries of the post types installed apps add",
              "products:read": "Read products, variants, images and bundles",
              "products:write": "Create, change and delete products and their variants",
              "inventory:read": "Read stock levels and stock locations",
              "inventory:write": "Adjust stock and manage stock locations",
              "collections:read": "Read collections and their rules",
              "collections:write": "Create, change and delete collections",
              "orders:read": "Read webshop orders, their items and payments",
              "orders:write": "Create orders and change their status",
              "customers:read": "Read customers and their addresses",
              "customers:write": "Create and change customers",
              "discounts:read": "Read discount codes",
              "discounts:write": "Create, change and delete discount codes",
              "shipping:read": "Read shipping labels and their track and trace codes",
              "leads:read": "Read form submissions and their notes",
              "leads:write": "Create, change and delete form submissions",
              "comments:read": "Read comments",
              "comments:write": "Moderate and delete comments",
              "modules:read": "Read the site modules and their settings",
              "modules:write": "Change module settings",
              "sites:read": "Read the site settings, notices and brand book",
              "sites:write": "Change the site settings, notices and brand book",
              "themes:read": "Read the active theme, its layouts and its design settings",
              "themes:write": "Change the theme design settings (colours, fonts) on the staging preview",
              "domains:read": "Read domains, DNS records and domain holders",
              "domains:write": "Change DNS records and domain holders",
              "domains:order": "Order domains, which may be charged to the merchant",
              "mail:read": "Read mailboxes and mailbox slots",
              "mail:write": "Create, change and delete mailboxes within the free slots",
              "mail:order": "Create mailboxes beyond the free slots, which is charged monthly",
              "billing:read": "Read the subscription, plan and invoices",
              "statistics:read": "Read site statistics and conversions",
              "webhooks:read": "Read this app's webhook subscriptions and the available topics",
              "app:settings:read": "Read this app's own settings on the site",
              "app:settings:write": "Change this app's own settings on the site"
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Content",
      "description": "Pages: their metadata, their element tree and their publish state.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "Domains",
      "description": "Domains, DNS and domain ordering.\n\nIn the help centre: [Domains and DNS](https://help.sitepack.eu/docs/domains/overview).",
      "externalDocs": {
        "description": "Domains and DNS",
        "url": "https://help.sitepack.eu/docs/domains/overview"
      }
    },
    {
      "name": "Elements",
      "description": "The element areas outside a page: the homepage, the footer and the sidebar. An area is replaced as a whole.\n\nIn the help centre: [Design and themes](https://help.sitepack.eu/docs/admin/design).",
      "externalDocs": {
        "description": "Design and themes",
        "url": "https://help.sitepack.eu/docs/admin/design"
      }
    },
    {
      "name": "Mail",
      "description": "Mailbox provisioning.\n\nIn the help centre: [Mail services](https://help.sitepack.eu/docs/mail/overview).",
      "externalDocs": {
        "description": "Mail services",
        "url": "https://help.sitepack.eu/docs/mail/overview"
      }
    },
    {
      "name": "Theme",
      "description": "The active theme and its layouts, read only - so an app knows what it renders into.\n\nIn the help centre: [Design and themes](https://help.sitepack.eu/docs/admin/design).",
      "externalDocs": {
        "description": "Design and themes",
        "url": "https://help.sitepack.eu/docs/admin/design"
      }
    },
    {
      "name": "Translations",
      "description": "Per-locale translations of pages, blogs, products and categories, plus the overviews that show what is missing or has gone stale.\n\nIn the help centre: [Translating content](https://help.sitepack.eu/docs/admin/content/translations).",
      "externalDocs": {
        "description": "Translating content",
        "url": "https://help.sitepack.eu/docs/admin/content/translations"
      }
    },
    {
      "name": "App settings",
      "description": "This app's own configuration on this site.\n\nIn the help centre: [Developing apps](https://help.sitepack.eu/docs/partners/apps).",
      "externalDocs": {
        "description": "Developing apps",
        "url": "https://help.sitepack.eu/docs/partners/apps"
      }
    },
    {
      "name": "Blogs",
      "description": "Blog posts. Publishing one also indexes it for site search.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "Categories",
      "description": "Content and product categories.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "Collections",
      "description": "Named groups of products, described by rules rather than a fixed membership list.\n\nIn the help centre: [Products](https://help.sitepack.eu/docs/admin/store/products).",
      "externalDocs": {
        "description": "Products",
        "url": "https://help.sitepack.eu/docs/admin/store/products"
      }
    },
    {
      "name": "Customers",
      "description": "Webshop customers. Personal data, behind its own scope.\n\nIn the help centre: [The webshop](https://help.sitepack.eu/docs/admin/store).",
      "externalDocs": {
        "description": "The webshop",
        "url": "https://help.sitepack.eu/docs/admin/store"
      }
    },
    {
      "name": "Discounts",
      "description": "Discount codes.\n\nIn the help centre: [Shop settings](https://help.sitepack.eu/docs/admin/store/settings).",
      "externalDocs": {
        "description": "Shop settings",
        "url": "https://help.sitepack.eu/docs/admin/store/settings"
      }
    },
    {
      "name": "Media",
      "description": "The media library. Metadata only - uploading bytes goes through the upload endpoint.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "Modules",
      "description": "The modules installed on the site and their settings. An app can configure a module but cannot install or remove one.\n\nIn the help centre: [Site settings](https://help.sitepack.eu/docs/admin/settings).",
      "externalDocs": {
        "description": "Site settings",
        "url": "https://help.sitepack.eu/docs/admin/settings"
      }
    },
    {
      "name": "Navigation",
      "description": "Menus and their items, including reordering.\n\nIn the help centre: [Menus and navigation](https://help.sitepack.eu/docs/admin/content/navigation).",
      "externalDocs": {
        "description": "Menus and navigation",
        "url": "https://help.sitepack.eu/docs/admin/content/navigation"
      }
    },
    {
      "name": "Orders",
      "description": "Webshop orders: reading them, creating them and moving them through their statuses.\n\nIn the help centre: [Orders](https://help.sitepack.eu/docs/admin/store/orders).",
      "externalDocs": {
        "description": "Orders",
        "url": "https://help.sitepack.eu/docs/admin/store/orders"
      }
    },
    {
      "name": "Post types",
      "description": "The post types installed apps add to a site, such as cinemas or branches. A post type and its fields come from the app, read only, with a JSON Schema of the fields; its entries are created, changed and deleted here.\n\nIn the help centre: [Content from apps](https://help.sitepack.eu/docs/admin/content/app-content-types).",
      "externalDocs": {
        "description": "Content from apps",
        "url": "https://help.sitepack.eu/docs/admin/content/app-content-types"
      }
    },
    {
      "name": "Products",
      "description": "The catalogue, its variants and its stock.\n\nIn the help centre: [Products](https://help.sitepack.eu/docs/admin/store/products).",
      "externalDocs": {
        "description": "Products",
        "url": "https://help.sitepack.eu/docs/admin/store/products"
      }
    },
    {
      "name": "Redirects",
      "description": "URL redirects.\n\nIn the help centre: [SEO and redirects](https://help.sitepack.eu/docs/admin/content/seo).",
      "externalDocs": {
        "description": "SEO and redirects",
        "url": "https://help.sitepack.eu/docs/admin/content/seo"
      }
    },
    {
      "name": "Shipping",
      "description": "Shipping labels, read only: buying one costs money at a carrier and cannot be undone.\n\nIn the help centre: [Shop settings](https://help.sitepack.eu/docs/admin/store/settings).",
      "externalDocs": {
        "description": "Shop settings",
        "url": "https://help.sitepack.eu/docs/admin/store/settings"
      }
    },
    {
      "name": "Site",
      "description": "Site settings and statistics.\n\nIn the help centre: [Site settings](https://help.sitepack.eu/docs/admin/settings).",
      "externalDocs": {
        "description": "Site settings",
        "url": "https://help.sitepack.eu/docs/admin/settings"
      }
    },
    {
      "name": "Tags",
      "description": "Tags.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "USPs",
      "description": "The site USPs (unique selling points): the small title, text and icon items the theme shows in the topbar and elsewhere. An ordered, site-level list.\n\nIn the help centre: [Managing content](https://help.sitepack.eu/docs/admin/content).",
      "externalDocs": {
        "description": "Managing content",
        "url": "https://help.sitepack.eu/docs/admin/content"
      }
    },
    {
      "name": "Webhooks",
      "description": "Which events this app is subscribed to, and every topic that exists.\n\nIn the help centre: [Developing apps](https://help.sitepack.eu/docs/partners/apps).",
      "externalDocs": {
        "description": "Developing apps",
        "url": "https://help.sitepack.eu/docs/partners/apps"
      }
    },
    {
      "name": "OAuth",
      "description": "Authorization: turning a merchant's consent into an access token, and refreshing or revoking it.\n\nIn the help centre: [Developing apps](https://help.sitepack.eu/docs/partners/apps).",
      "externalDocs": {
        "description": "Developing apps",
        "url": "https://help.sitepack.eu/docs/partners/apps"
      }
    },
    {
      "name": "Meta",
      "description": "Who am I, and what am I allowed to do? Any valid app token may call these.\n\nIn the help centre: [Developing apps](https://help.sitepack.eu/docs/partners/apps).",
      "externalDocs": {
        "description": "Developing apps",
        "url": "https://help.sitepack.eu/docs/partners/apps"
      }
    },
    {
      "name": "Leads",
      "description": "Leads: the form submissions visitors send through the site's forms, with their notes. A new one fires the `lead.created` webhook.\n\nIn the help centre: [Leads](https://help.sitepack.eu/docs/admin/leads)."
    },
    {
      "name": "Comments",
      "description": "Comments and reviews visitors leave on the site, and their moderation."
    },
    {
      "name": "Billing",
      "description": "The site's own SitePack invoices and subscriptions, read only."
    }
  ],
  "externalDocs": {
    "description": "Developing apps for SitePack",
    "url": "https://help.sitepack.eu/docs/partners/apps"
  },
  "servers": [
    {
      "url": "https://api.sitepack.eu",
      "description": "Production"
    }
  ],
  "x-tagGroups": [
    {
      "name": "Getting started",
      "tags": [
        "OAuth",
        "Meta",
        "App settings",
        "Webhooks"
      ]
    },
    {
      "name": "Leads & customers",
      "tags": [
        "Leads",
        "Customers",
        "Comments"
      ]
    },
    {
      "name": "Webshop",
      "tags": [
        "Products",
        "Collections",
        "Orders",
        "Discounts",
        "Shipping"
      ]
    },
    {
      "name": "Content",
      "tags": [
        "Content",
        "Blogs",
        "Post types",
        "Elements",
        "Navigation",
        "Translations",
        "Media",
        "Categories",
        "Tags",
        "Redirects",
        "USPs",
        "Theme"
      ]
    },
    {
      "name": "Site",
      "tags": [
        "Site",
        "Modules",
        "Billing",
        "Domains",
        "Mail"
      ]
    }
  ]
}
