{
  "openapi": "3.1.0",
  "info": {
    "title": "btab API",
    "version": "1.0.0",
    "description": "btab's public partner API: browse the catalogue, manage the products in your store, handle orders, subscribe to webhooks, and connect apps with OAuth.\n\n## Authentication\nSend `Authorization: Bearer <credential>` on every request. The credential is either a **vendor API key** (`btab_...`, created in the vendor dashboard under Settings \u2192 API keys) or an **OAuth 2.1 access token** issued to an app the vendor connected (authorization code + PKCE, see the OAuth endpoints). Both act as one store.\n\n## Scopes\nEvery credential carries one of two scopes. `read` may make `GET`/`HEAD` requests only; any other method is refused with `403` (`required_scope: read_write`). `read_write` may do everything. The scope rule is by HTTP method, so each operation's `x-btab-scope` is exactly what the server enforces.\n\n**Partner apps** (an app btab has registered, installed by a store over OAuth) are granted **resource scopes** instead: `read_products`, `write_products`, `read_inventory`, `write_inventory`, `read_orders`, `write_orders`, `read_customers` and `write_external_orders`. A write scope includes its read; `read_customers` (buyer names, emails, addresses) is never implied, and without it those fields are left out of order answers. An app token works only on the operations below that carry `x-btab-app-scope`, with that scope; anywhere else it is refused. It is audience-bound to `https://api.btab.app/api/v1` (`resource` at authorize and token), never the MCP endpoint. Each app has a per-store request limit (default 120 a minute; `429` with `Retry-After` over it). Scopes are read from the grant on every call, so a store that re-installs an app with fewer scopes narrows it from the next request (the token response's `scope` is informational). Creating orders is not part of `write_orders`: an app creates orders only through the external-orders endpoint, with `write_external_orders`.\n\n## Errors\nErrors are JSON `{ \"error\": <category>, \"message\": <text> }`, sometimes with a machine-readable `code`. OAuth endpoints use RFC 6749 `{ error, error_description }`.\n\n## Rate limits\nSelected endpoints are rate-limited (e.g. OAuth token 60/min, client registration 20/hour, discovery 60/min per IP; AI and import endpoints separately). A limited request gets `429` with `Retry-After` and `X-RateLimit-*` headers. There is no global per-credential quota today; be a good citizen and back off on `429`.\n\n## Inventory\nStock is part of the product. Read `in_stock` and `stock_quantity` on catalogue products and listings, and per variant (`quantity_available`, `available_for_sale`) on `GET /api/v1/my-products/{productId}`. To follow changes, subscribe to the `inventory.updated` webhook. It carries ids only, so re-read the product when it arrives.\n\nTo keep a marketplace in step, read `GET /api/v1/inventory`: stock available to sell per listed product and option, with a change cursor (`updated_since`). **Planned:** adjusting stock from outside (see `x-btab-planned`).\n\n## Money\nAll amounts are integer minor units (cents), AUD unless stated.\n\nGuides and examples: https://btab.app/developers",
    "contact": {
      "name": "btab developers",
      "url": "https://btab.app/developers"
    }
  },
  "externalDocs": {
    "description": "Developer guides",
    "url": "https://btab.app/developers"
  },
  "servers": [
    {
      "url": "https://api.btab.app"
    }
  ],
  "tags": [
    {
      "name": "Catalogue",
      "description": "Everything you can sell: approved products, with stock and your earnings."
    },
    {
      "name": "Store",
      "description": "The store the credential belongs to."
    },
    {
      "name": "Listings",
      "description": "The products in your store and how you sell them."
    },
    {
      "name": "Orders",
      "description": "Orders on your store, fulfilment of your own goods, invoices."
    },
    {
      "name": "Webhooks",
      "description": "Signed event deliveries to your HTTPS endpoint."
    },
    {
      "name": "OAuth",
      "description": "Connect an app to a store (OAuth 2.1, authorization code + PKCE, public clients)."
    }
  ],
  "x-btab-public-prefixes": [
    "/api/v1/inventory",
    "/api/v1/my-products",
    "/api/v1/orders",
    "/api/v1/vendor/webhooks",
    "/oauth",
    "/.well-known"
  ],
  "x-btab-excluded": [
    {
      "method": "GET",
      "path": "/api/v1/my-products/without-descriptions",
      "reason": "Dashboard helper feeding bulk AI description generation."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/generate-descriptions-batch",
      "reason": "AI generation: consumes BTAB AI credits, dashboard-only."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/{productId}/generate-description",
      "reason": "AI generation: consumes BTAB AI credits, dashboard-only."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/{productId}/description",
      "reason": "Custom-copy editor read; the effective description is already on the listing."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/{productId}/description",
      "reason": "Custom-copy editor write; dashboard workflow (tied to the AI draft review gate)."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/{productId}/seo",
      "reason": "Search-engine-listing editor read (SEO4); the resolved meta is already on the storefront payload."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/{productId}/seo",
      "reason": "Search-engine-listing editor write (SEO4; SEO c adds the URL handle); dashboard workflow \u2014 agents use the MCP tool set_product_search_listing."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/seo-hidden",
      "reason": "Bulk hide-from-search toggle (SEO4) from the product list; dashboard workflow."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/drafts",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/drafts/approve",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/drafts/{productId}",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/drafts/{productId}/approve",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/drafts/{productId}/discard",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/drafts/{productId}/revert",
      "reason": "AI-description review queue: dashboard workflow tied to BTAB AI credits, not a partner integration surface."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/{productId}/available-images",
      "reason": "Per-listing image curation from the supplier pool: dashboard UI workflow; not yet part of the stable partner contract."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/{productId}/images",
      "reason": "Per-listing image curation from the supplier pool: dashboard UI workflow; not yet part of the stable partner contract."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/{productId}/images",
      "reason": "Per-listing image curation from the supplier pool: dashboard UI workflow; not yet part of the stable partner contract."
    },
    {
      "method": "DELETE",
      "path": "/api/v1/my-products/{productId}/images/{imageId}",
      "reason": "Per-listing image curation from the supplier pool: dashboard UI workflow; not yet part of the stable partner contract."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/{productId}/images/reorder",
      "reason": "Per-listing image curation from the supplier pool: dashboard UI workflow; not yet part of the stable partner contract."
    },
    {
      "method": "PUT",
      "path": "/api/v1/my-products/{productId}/images/{imageId}/alt",
      "reason": "A store's alt text for one listing photo (SEO a): dashboard image-curation workflow; agents use the MCP tool set_product_image_alt."
    },
    {
      "method": "POST",
      "path": "/api/v1/my-products/{productId}/images/alt-suggestions",
      "reason": "AI generation (SEO a): a vision call against the platform AI spend cap, dashboard-only; nothing is saved."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/channels",
      "reason": "Dashboard analytics tile (sales by acquisition channel); shape may change."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/manual",
      "reason": "Counter-sale / manual order entry for the in-store dashboard; not a partner flow yet."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/delivery-quote-links",
      "reason": "Dashboard helper: courier comparison links for the arrange-delivery step."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/purchase-orders",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/send",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "PATCH",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/status",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/receive",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/cancel",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/replies/{replyId}/apply",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/replies/{replyId}/dismiss",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/document",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/edits",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/email",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "PUT",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/email",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/email/preview",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/buyer-messages",
      "reason": "Contact buyer from the dashboard, version 1: a dashboard feature behind a switch (contact_buyer_enabled), sent by a signed-in person only; not part of the public API yet."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/buyer-messages",
      "reason": "Contact buyer from the dashboard, version 1: a dashboard feature behind a switch (contact_buyer_enabled), sent by a signed-in person only; not part of the public API yet."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/buyer-messages/preview",
      "reason": "Contact buyer from the dashboard, version 1: a dashboard feature behind a switch (contact_buyer_enabled), sent by a signed-in person only; not part of the public API yet."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/products",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/supplier",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/purchase-orders/{poId}/supplier/undo",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/suppliers/{supplierId}/purchase-order",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/suppliers/{supplierId}/own-stock",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/suppliers/{supplierId}/own-stock/undo",
      "reason": "Procure-to-order purchase orders: internal supplier workflow (Lane C), still moving."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/parts/{partId}/arrange-delivery",
      "reason": "Freight arrangement for a fulfilment part: dashboard workflow, still moving."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/parts/{partId}/removalist-link",
      "reason": "Dashboard helper: the signed no-login delivery link the store texts its removalist."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/push-accounting",
      "reason": "Manual retry of the accounting-connector push; driven by the connected integration, not a partner call."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/push-xero",
      "reason": "Deprecated alias of push-accounting (removed in M9)."
    },
    {
      "method": "GET",
      "path": "/.well-known/oauth-protected-resource/api/v1/vendor/mcp",
      "reason": "Path-inserted RFC 9728 form of the same document as /.well-known/oauth-protected-resource (documented there)."
    },
    {
      "method": "GET",
      "path": "/oauth/consent-details",
      "reason": "Consent-screen data for the BTAB dashboard; takes a signed transaction blob, not a partner endpoint."
    },
    {
      "method": "POST",
      "path": "/oauth/consent",
      "reason": "Dashboard-session only: the vendor approves/denies on the BTAB consent screen."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/lookup",
      "reason": "Counter (till) scan lookup by SKU/barcode (R2-a); dashboard workflow."
    },
    {
      "method": "GET",
      "path": "/api/v1/my-products/search-for-sale",
      "reason": "Product picker on the dashboard's New order form (one row per option, thumbnail, store price); dashboard workflow."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/counter/freight-quote",
      "reason": "Counter (till) delivery price from the store's rate card (R2-a); dashboard workflow."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/receipt",
      "reason": "Counter (till) 80mm receipt HTML for printing (R2-a); the A4 tax invoice is /orders/{id}/invoice."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/payments",
      "reason": "Order payments ledger (R2-d deposits / part payments) for the dashboard order view and till; dashboard workflow."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/payments",
      "reason": "Take a payment against an order's balance at the counter (R2-d); dashboard till workflow."
    },
    {
      "method": "GET",
      "path": "/api/v1/orders/{id}/refunds",
      "reason": "Store-side refunds (returns #5): options + history for the dashboard order panel."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/refunds/preview",
      "reason": "Store-side refunds (returns #5): amount preview for the dashboard; no side effects."
    },
    {
      "method": "POST",
      "path": "/api/v1/orders/{id}/refunds",
      "reason": "Store-side partial refund (returns #5): moves money on the store's own Stripe \u2014 dashboard workflow with explicit confirmation."
    }
  ],
  "paths": {
    "/api/v1/products": {
      "get": {
        "operationId": "listProducts",
        "summary": "Browse the catalogue",
        "description": "Approved, sellable catalogue products, with stock (`in_stock`, `stock_quantity`), your earn per sale and your minimum price. Unknown query keys are stripped.",
        "tags": [
          "Catalogue"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_products",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Full-text search (falls back to substring match).",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "in_stock",
            "in": "query",
            "required": false,
            "description": "Only in-stock products.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "pricing_type",
            "in": "query",
            "required": false,
            "description": "Pricing model.",
            "schema": {
              "type": "string",
              "enum": [
                "wholesale",
                "commission"
              ]
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Only this supplier.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "brand",
            "in": "query",
            "required": false,
            "description": "Brand.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Category slug or raw value.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "store_type",
            "in": "query",
            "required": false,
            "description": "Taxonomy vertical, or `_other`.",
            "schema": {
              "type": "string",
              "maxLength": 80
            }
          },
          {
            "name": "exclude_in_store",
            "in": "query",
            "required": false,
            "description": "Hide products already in your store.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "purchase_mode",
            "in": "query",
            "required": false,
            "description": "`buy_now` = card checkout only, `enquiry` = quote-only.",
            "schema": {
              "type": "string",
              "enum": [
                "buy_now",
                "enquiry"
              ]
            }
          },
          {
            "name": "price_min_cents",
            "in": "query",
            "required": false,
            "description": "Retail price lower bound (inclusive).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "price_max_cents",
            "in": "query",
            "required": false,
            "description": "Retail price upper bound (exclusive).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "ready_to_list",
            "in": "query",
            "required": false,
            "description": "Only products marked ready by the nightly readiness pass.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "complete_for",
            "in": "query",
            "required": false,
            "description": "Only products the nightly completeness pass scored 100% complete for this channel (storefront, google, meta, freight). An uncomputed channel matches nothing.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{0,31}$"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "description": "Store types: `store` = only this store's departments (its store type), plus its own makers and products not yet sorted into a category; `all` = everything. Omit for the platform default (`catalog_scope_default`, everything until a store is switched). A read filter, never a permission.",
            "schema": {
              "type": "string",
              "enum": [
                "store",
                "all"
              ]
            }
          },
          {
            "name": "ownership",
            "in": "query",
            "required": false,
            "description": "For a store whose account has its own suppliers (`own_suppliers` in the facets answer): `own` = only products of the store's own suppliers; `btab` = every other product the store may see; `all` = both. Omit for both. The answer then carries `ownership: { applied, choices }`. Ignored for every other store: same products, no extra field. A read filter, never a permission.",
            "schema": {
              "type": "string",
              "enum": [
                "own",
                "btab",
                "all"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (1-based).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 20
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "Sort column. Omit to rank by relevance (search) or the platform default.",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "created_at",
                "updated_at",
                "stock_quantity",
                "retail_price_cents",
                "wholesale_price_cents",
                "sku",
                "margin",
                "readiness",
                "recommended"
              ]
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/products/facets": {
      "get": {
        "operationId": "getCatalogFacets",
        "summary": "Catalogue filter facets",
        "description": "Value + count lists for every catalogue filter, over the same filter set as listProducts.",
        "tags": [
          "Catalogue"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_products",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Full-text search (falls back to substring match).",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "in_stock",
            "in": "query",
            "required": false,
            "description": "Only in-stock products.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "pricing_type",
            "in": "query",
            "required": false,
            "description": "Pricing model.",
            "schema": {
              "type": "string",
              "enum": [
                "wholesale",
                "commission"
              ]
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Only this supplier.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "brand",
            "in": "query",
            "required": false,
            "description": "Brand.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Category slug or raw value.",
            "schema": {
              "type": "string",
              "maxLength": 120
            }
          },
          {
            "name": "store_type",
            "in": "query",
            "required": false,
            "description": "Taxonomy vertical, or `_other`.",
            "schema": {
              "type": "string",
              "maxLength": 80
            }
          },
          {
            "name": "exclude_in_store",
            "in": "query",
            "required": false,
            "description": "Hide products already in your store.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "purchase_mode",
            "in": "query",
            "required": false,
            "description": "`buy_now` = card checkout only, `enquiry` = quote-only.",
            "schema": {
              "type": "string",
              "enum": [
                "buy_now",
                "enquiry"
              ]
            }
          },
          {
            "name": "price_min_cents",
            "in": "query",
            "required": false,
            "description": "Retail price lower bound (inclusive).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "price_max_cents",
            "in": "query",
            "required": false,
            "description": "Retail price upper bound (exclusive).",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          },
          {
            "name": "ready_to_list",
            "in": "query",
            "required": false,
            "description": "Only products marked ready by the nightly readiness pass.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "complete_for",
            "in": "query",
            "required": false,
            "description": "Only products the nightly completeness pass scored 100% complete for this channel (storefront, google, meta, freight). An uncomputed channel matches nothing.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{0,31}$"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "description": "Same as on GET /api/v1/products: the facet counts follow the grid's scope.",
            "schema": {
              "type": "string",
              "enum": [
                "store",
                "all"
              ]
            }
          },
          {
            "name": "ownership",
            "in": "query",
            "required": false,
            "description": "Same as on GET /api/v1/products: the facet counts follow the grid's ownership choice. Ignored for a store without own suppliers.",
            "schema": {
              "type": "string",
              "enum": [
                "own",
                "btab",
                "all"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Facets.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogFacets"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/products/{id}": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get a catalogue product",
        "description": "One product with images, options, variants (each with its own `in_stock` / `stock_quantity`), assembled dimensions and drawings.",
        "tags": [
          "Catalogue"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_products",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/me": {
      "get": {
        "operationId": "getStore",
        "summary": "Get the authenticated store",
        "description": "The store the credential belongs to, with order statistics.",
        "tags": [
          "Store"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "*",
        "responses": {
          "200": {
            "description": "The store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VendorProfile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products": {
      "get": {
        "operationId": "listListings",
        "summary": "List products in my store",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_products",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (1-based).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 20
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Search this store's own listings.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "missing",
            "in": "query",
            "required": false,
            "description": "Only listings missing shipping data: `weight`, `dimensions`, or `either`. Unrecognised values are ignored.",
            "schema": {
              "type": "string",
              "enum": [
                "weight",
                "dimensions",
                "either"
              ]
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "`variants` adds each listing's option group titles (`options`) and its variants' identity and stock (`variants`).",
            "schema": {
              "type": "string",
              "enum": [
                "variants"
              ]
            }
          },
          {
            "name": "own_goods",
            "in": "query",
            "required": false,
            "description": "`true` = only the store's own products (`own_goods: true`); `total_count` and pagination count the same set.",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of listings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "addListing",
        "summary": "Add a catalogue product to my store",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddListingRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddListingResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/bulk": {
      "post": {
        "operationId": "bulkAddListings",
        "summary": "Bulk-add catalogue products",
        "description": "Adds at default pricing. `ids` mode reports per-id skips; `filter` mode adds every eligible match.",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkListingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkAddResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/bulk-remove": {
      "post": {
        "operationId": "bulkRemoveListings",
        "summary": "Bulk-remove products from my store",
        "description": "Soft delete; re-adding restores. `mode: \"filter\"` with `filters: {}` clears the whole store.",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkListingRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkRemoveResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/{productId}": {
      "get": {
        "operationId": "getListing",
        "summary": "Get one product in my store",
        "description": "The listing with your effective price, options and variants (each with `available_for_sale` / `quantity_available`).",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_products",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "description": "Catalogue product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "removeListing",
        "summary": "Remove a product from my store",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "description": "Catalogue product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/{productId}/pricing": {
      "put": {
        "operationId": "updateListingPricing",
        "summary": "Set or clear my custom retail price",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "description": "Catalogue product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PricingUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingUpdateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/{productId}/enquiry-mode": {
      "patch": {
        "operationId": "updateListingEnquiryMode",
        "summary": "Set the selling mode of a listing",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "description": "Catalogue product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnquiryModeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EnquiryModeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/my-products/{productId}/vendor-fulfilled": {
      "patch": {
        "operationId": "updateListingVendorFulfilled",
        "summary": "Mark a listing as fulfilled by me",
        "description": "Own goods: the vendor ships this listing themselves.",
        "tags": [
          "Listings"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_products",
        "parameters": [
          {
            "name": "productId",
            "in": "path",
            "required": true,
            "description": "Catalogue product id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VendorFulfilledRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VendorFulfilledResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders": {
      "get": {
        "operationId": "listOrders",
        "summary": "List my orders",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_orders",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Order status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "processing",
                "fulfilled",
                "shipped",
                "delivered",
                "cancelled"
              ]
            }
          },
          {
            "name": "start_date",
            "in": "query",
            "required": false,
            "description": "ISO date or date-time lower bound on created_at.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "end_date",
            "in": "query",
            "required": false,
            "description": "ISO date or date-time upper bound; a bare date includes that whole day.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Order number, buyer name or buyer email.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "attention",
            "in": "query",
            "required": false,
            "description": "`1` = only orders needing action.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "Where the buyer came from.",
            "schema": {
              "type": "string",
              "enum": [
                "facebook_instagram",
                "google",
                "vendor_link",
                "email",
                "other_referral",
                "direct",
                "unknown"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "Page number (1-based).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 20
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "required": false,
            "description": "Sort column.",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "updated_at",
                "id"
              ],
              "default": "created_at"
            }
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "Sort direction.",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of orders.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createOrder",
        "summary": "Place a wholesale order",
        "description": "The vendor buys catalogue products from BTAB at wholesale (`order_type: wholesale_api`). Products must be active, have images, and have enough stock.",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrderRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateOrderResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/inventory": {
      "get": {
        "operationId": "listInventory",
        "x-btab-app-scope": "read_inventory",
        "summary": "Stock available to sell, by what changed",
        "description": "For a marketplace app keeping the store's stock right on the marketplace. One item per listed product (options nested), oldest change first. Pass back next_cursor as updated_since to read only what changed since. A listing the store switched off comes back with listed: false. Answers 404 while the platform has this read switched off. Rate limit: the store's catalogue-read limit.",
        "tags": [
          "Products"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "parameters": [
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "description": "A next_cursor from an earlier page. Absent = from the beginning.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Products per page (at most the platform's page size, 500 by default).",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of inventory.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InventoryPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/external": {
      "post": {
        "operationId": "createExternalOrder",
        "x-btab-app-scope": "write_external_orders",
        "summary": "Post a marketplace order",
        "description": "For a marketplace app (the Kogan app). The order arrives like an order imported from the store's own Shopify: paid on the marketplace (the store collected it), no emails to the buyer, supplier purchase orders as drafts for the store to send, and nothing in btab's ledger. Only the store's own goods: an order with a line btab supplies, or a SKU the store has not mapped, is held (202) and imports when it is posted again after the SKU is mapped. Posting the same order twice, or an order that already came in through the store's Shopify, returns the existing order (200). Rate-limited per key. Answers 404 while the platform has external orders switched off.",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExternalOrderRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already in btab: the existing order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalOrderResponse"
                }
              }
            }
          },
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalOrderResponse"
                }
              }
            }
          },
          "202": {
            "description": "Held: not created yet (an unmapped SKU, or a btab-supplied line).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalOrderResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/external/{id}/cancel": {
      "post": {
        "tags": [
          "Orders"
        ],
        "operationId": "cancelExternalOrder",
        "summary": "Cancel an order a marketplace app brought in",
        "description": "For a marketplace app (the Kogan app): the buyer cancelled on the marketplace. Works only on an order posted through POST /api/v1/orders/external (its source is a marketplace app's); any other order of the store answers 403 NOT_AN_APP_ORDER. Always recorded as reason=marketplace: stock comes back for lines not shipped, and the order's supplier purchase orders that were never sent are cancelled (a sent one is kept and listed). 404 while the store's marketplace orders are switched off.",
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_external_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderMutationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "summary": "Get an order",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/fulfilment": {
      "get": {
        "operationId": "getOrderFulfilment",
        "summary": "Get my fulfilment part of an order",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": "read_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The part (null when the order has no own-goods part).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FulfilmentResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/invoice": {
      "get": {
        "operationId": "getOrderInvoice",
        "summary": "Download the buyer's tax invoice (PDF)",
        "description": "Issued when the order ships; 404 before then. Rendered fresh from the frozen invoice number, so every download is the same document.",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "x-btab-app-scope": [
          "read_orders",
          "read_customers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice PDF (`Content-Disposition: inline`).",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "contentMediaType": "application/pdf"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/accept": {
      "post": {
        "operationId": "acceptOrder",
        "summary": "Accept my own-goods part",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Advanced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FulfilmentActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/ship": {
      "post": {
        "operationId": "shipOrder",
        "summary": "Mark my own-goods part shipped",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShipRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Advanced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FulfilmentActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/deliver": {
      "post": {
        "operationId": "deliverOrder",
        "summary": "Mark my own-goods part delivered",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Advanced.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FulfilmentActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/status": {
      "patch": {
        "operationId": "updateOrderStatus",
        "summary": "Set an order status",
        "description": "`cancelled` runs the same cancellation as cancelOrder (restock + ledger reversal).",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateOrderStatusRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderMutationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/orders/{id}/cancel": {
      "post": {
        "operationId": "cancelOrder",
        "summary": "Cancel an order",
        "description": "Refused (400) once an order is fulfilled, shipped, delivered or already cancelled.",
        "tags": [
          "Orders"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "x-btab-app-scope": "write_orders",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "enum": [
                      "store",
                      "customer",
                      "marketplace"
                    ],
                    "description": "Who cancelled. Absent = a plain cancel, as before. \"marketplace\" (the buyer cancelled on the marketplace before dispatch) also cancels the order's supplier purchase orders that were never sent; a sent one is kept and listed in purchase_orders.kept."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderMutationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/webhooks": {
      "get": {
        "operationId": "listWebhookSubscriptions",
        "summary": "List webhook subscriptions",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "responses": {
          "200": {
            "description": "Subscriptions and the valid topic set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createWebhookSubscription",
        "summary": "Create a webhook subscription",
        "description": "Returns the signing secret exactly once. Max 10 subscriptions per store.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSubscriptionCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/webhooks/{id}": {
      "get": {
        "operationId": "getWebhookSubscription",
        "summary": "Get a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "operationId": "updateWebhookSubscription",
        "summary": "Update a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookSubscriptionUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhookSubscription",
        "summary": "Delete a webhook subscription",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/webhooks/{id}/rotate-secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "summary": "Rotate the signing secret",
        "description": "The old secret stops signing immediately. The new one is shown exactly once.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSecretRotated"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/webhooks/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List delivery attempts",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Last 100 attempts, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/vendor/webhooks/{id}/deliveries/{deliveryId}/resend": {
      "post": {
        "operationId": "resendWebhookDelivery",
        "summary": "Re-send one event",
        "description": "Queues a redelivery with the same `event_key`; attempt numbering continues. 409 when the subscription is disabled.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearer": []
          }
        ],
        "x-btab-scope": "read_write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Webhook subscription id.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "description": "Delivery id.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookResendResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "getAuthorizationServerMetadata",
        "summary": "Authorization server metadata (RFC 8414)",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "responses": {
          "200": {
            "description": "Metadata. Cacheable for 5 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizationServerMetadata"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "operationId": "getProtectedResourceMetadata",
        "summary": "Protected resource metadata (RFC 9728)",
        "description": "Also served at the path-inserted form `/.well-known/oauth-protected-resource/api/v1/vendor/mcp` (same document).",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "responses": {
          "200": {
            "description": "Metadata. Cacheable for 5 minutes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProtectedResourceMetadata"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/oauth/register": {
      "post": {
        "operationId": "registerOAuthClient",
        "summary": "Register an OAuth client (RFC 7591)",
        "description": "Fallback for clients that cannot use a client ID metadata document (an https URL as `client_id`, preferred). Anonymous and rate-limited (20/hour per IP). 403 when dynamic registration is disabled by policy.",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClientRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientRegistrationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "403": {
            "$ref": "#/components/responses/OAuthError"
          },
          "404": {
            "$ref": "#/components/responses/OAuthNotEnabled"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "operationId": "authorize",
        "summary": "Start an authorization (authorization code + PKCE)",
        "description": "Validates the request and redirects the browser to the BTAB dashboard consent screen, where the vendor approves. After approval the browser is redirected to `redirect_uri` with `code` and `state`. Errors before the client and redirect_uri are validated are returned as JSON (never redirected); later errors are redirected with `error` / `error_description`.",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "const": "code"
            }
          },
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "description": "A client ID metadata document URL (https) or a registered `dcr_...` id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "description": "Exact match against the registered set (loopback: any port).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "const": "S256"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "read",
                "read_write"
              ],
              "default": "read"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resource",
            "in": "query",
            "required": false,
            "description": "RFC 8707; must equal this server's resource URL.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the consent screen, or back to `redirect_uri` with an error.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "404": {
            "$ref": "#/components/responses/OAuthNotEnabled"
          },
          "503": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "operationId": "exchangeToken",
        "summary": "Exchange a code or refresh token for tokens",
        "description": "Public clients only (no client secret). Rate-limited to 60/minute per IP.",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tokens.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "404": {
            "$ref": "#/components/responses/OAuthNotEnabled"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "operationId": "revokeOAuthToken",
        "summary": "Revoke a token (RFC 7009): an app disconnects itself",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "x-btab-scope": "none",
        "description": "Public clients authenticate with `client_id` (token endpoint auth method `none`). A token is only revocable by the client it was issued to.\n\n- **Refresh token:** ends the whole connection. The grant, its credential and every access token stop working at once. This is how an app uninstalls itself.\n- **Access token:** revokes that token only.\n\nAn unknown, expired or already-revoked token also answers `200`, per RFC 7009 \u00a72.2. Rate limit: 60 requests per minute per IP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "client_id"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "The access or refresh token to revoke."
                  },
                  "token_type_hint": {
                    "type": "string",
                    "enum": [
                      "access_token",
                      "refresh_token"
                    ],
                    "description": "Optional; the server looks the token up either way."
                  },
                  "client_id": {
                    "type": "string",
                    "description": "The client the token was issued to."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked, or the token was already unusable. Empty body."
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "description": "`invalid_client`: the token was issued to a different client. Nothing is revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "const": "invalid_client"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "OAuth is not enabled on this server."
          },
          "429": {
            "description": "Rate limited."
          }
        }
      }
    }
  },
  "webhooks": {
    "order.created": {
      "post": {
        "operationId": "webhookOrderCreated",
        "summary": "An order was placed on your store",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "order.shipped": {
      "post": {
        "operationId": "webhookOrderShipped",
        "summary": "An order moved to shipped",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_shipped": {
                  "value": {
                    "event": "order.shipped",
                    "event_key": "order.shipped:4211",
                    "created_at": "2026-08-29T04:00:00.201Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "shipped",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-29T04:00:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": "2026-08-29T04:00:00.000Z",
                        "delivered_at": null,
                        "invoice_template": "au-ato",
                        "invoice_number": "INV-000321",
                        "invoice_issued_at": "2026-08-29T04:00:00.000Z"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "order.fulfilled": {
      "post": {
        "operationId": "webhookOrderFulfilled",
        "summary": "An order moved to fulfilled",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_fulfilled": {
                  "value": {
                    "event": "order.fulfilled",
                    "event_key": "order.fulfilled:4211",
                    "created_at": "2026-08-29T01:00:00.201Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "fulfilled",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-29T04:00:00.000Z",
                        "fulfilled_at": "2026-08-29T04:00:00.000Z",
                        "shipped_at": null,
                        "delivered_at": null,
                        "invoice_template": "au-ato"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "order.completed": {
      "post": {
        "operationId": "webhookOrderCompleted",
        "summary": "An order was delivered (completed)",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_completed": {
                  "value": {
                    "event": "order.completed",
                    "event_key": "order.completed:4211",
                    "created_at": "2026-09-01T03:00:00.201Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "delivered",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-29T04:00:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": "2026-08-29T04:00:00.000Z",
                        "delivered_at": "2026-09-01T03:00:00.000Z",
                        "invoice_template": "au-ato"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "site.published": {
      "post": {
        "operationId": "webhookSitePublished",
        "summary": "Your storefront was published",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SitePublishedEvent"
              },
              "examples": {
                "site_published": {
                  "value": {
                    "event": "site.published",
                    "event_key": "site.published:52",
                    "created_at": "2026-08-20T09:30:00.000Z",
                    "data": {
                      "site": {
                        "id": 52,
                        "slug": "oak-and-iron",
                        "url": "https://oak-and-iron.btab.app",
                        "is_published": true,
                        "published_at": "2026-08-20T09:30:00.000Z"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "product.created": {
      "post": {
        "operationId": "webhookProductCreated",
        "summary": "You listed a product in your store",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogEvent"
              },
              "examples": {
                "product_created": {
                  "value": {
                    "event": "product.created",
                    "event_key": "product.created:8812.1756432800123",
                    "created_at": "2026-08-29T02:00:00.123Z",
                    "data": {
                      "product_id": 8812
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "product.updated": {
      "post": {
        "operationId": "webhookProductUpdated",
        "summary": "A product in your store changed",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogEvent"
              },
              "examples": {
                "product_updated": {
                  "value": {
                    "event": "product.updated",
                    "event_key": "product.updated:8812.1756432800123",
                    "created_at": "2026-08-29T02:00:00.123Z",
                    "data": {
                      "product_id": 8812
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "product.deleted": {
      "post": {
        "operationId": "webhookProductDeleted",
        "summary": "A product left your store",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogEvent"
              },
              "examples": {
                "product_deleted": {
                  "value": {
                    "event": "product.deleted",
                    "event_key": "product.deleted:8812.1756432800123",
                    "created_at": "2026-08-29T02:00:00.123Z",
                    "data": {
                      "product_id": 8812
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "inventory.updated": {
      "post": {
        "operationId": "webhookInventoryUpdated",
        "summary": "Stock of a product in your store changed",
        "description": "Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CatalogEvent"
              },
              "examples": {
                "inventory_updated": {
                  "value": {
                    "event": "inventory.updated",
                    "event_key": "inventory.updated:8812.1756432800123",
                    "created_at": "2026-08-29T02:00:00.123Z",
                    "data": {
                      "product_id": 8812,
                      "variant_id": 30114
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "order.paid": {
      "post": {
        "operationId": "webhookOrderPaid",
        "summary": "An order was paid",
        "description": "Fires once, on the first paid transition: a card payment captured, or an invoice order marked paid. Partner apps need read_orders; buyer fields only with read_customers. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "order.refunded": {
      "post": {
        "operationId": "webhookOrderRefunded",
        "summary": "An order was refunded in full",
        "description": "Fires once, when the whole order is refunded. Partner apps need read_orders; buyer fields only with read_customers. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "app.uninstalled": {
      "post": {
        "operationId": "webhookAppUninstalled",
        "summary": "The store removed your app (partner apps only)",
        "description": "The last delivery an install gets: its grant and every token are already revoked. `data`: `{ store_id, install_id }`. Sent to every partner app whatever its scopes; a store cannot subscribe to it. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "customers.data_request": {
      "post": {
        "operationId": "webhookCustomersDataRequest",
        "summary": "A buyer asked for their data (partner apps only)",
        "description": "Send the store what you hold about this buyer. `data`: `{ store_id, customer: { email } }`. Mandatory: sent to every installed app whatever its scopes. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "customers.redact": {
      "post": {
        "operationId": "webhookCustomersRedact",
        "summary": "A buyer asked for their data to be erased (partner apps only)",
        "description": "Erase what you hold about this buyer for this store. `data`: `{ store_id, customer: { email } }`. Mandatory: sent to every installed app whatever its scopes. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    },
    "shop.redact": {
      "post": {
        "operationId": "webhookShopRedact",
        "summary": "The store asked to be deleted (partner apps only)",
        "description": "Erase what you hold for this store. `data`: `{ store_id, deletion_scheduled_at }`. Mandatory: sent to every installed app whatever its scopes. Every delivery is a POST with `Content-Type: application/json` and the headers `X-Btab-Event: <topic>`, `X-Btab-Delivery-Id: <subscription>:<event_key>:<attempt>` and `X-Btab-Signature: t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with your whsec_ secret>` (the Stripe scheme \u2014 verify it over the RAW body, with a replay window). Respond 2xx within 10 s; redirects are not followed; 410 Gone disables the subscription. Failures retry 1m \u2192 5m \u2192 30m \u2192 2h \u2192 12h. Ordering is not guaranteed and a delivery may repeat: dedupe on `event_key`. See docs/api/WEBHOOKS.md.",
        "tags": [
          "Webhooks"
        ],
        "security": [],
        "parameters": [
          {
            "name": "X-Btab-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Btab-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`t=<unix seconds>, v1=<hex HMAC-SHA256>`"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderEvent"
              },
              "examples": {
                "order_created": {
                  "value": {
                    "event": "order.created",
                    "event_key": "order.created:4211",
                    "created_at": "2026-08-28T02:10:00.512Z",
                    "data": {
                      "order": {
                        "id": 4211,
                        "vendor_id": 17,
                        "order_number": "ORD-20260828-4F2A9C",
                        "total_cents": 12990,
                        "shipping_cents": 1500,
                        "charge_total_cents": 14490,
                        "tax_amount_cents": 1317,
                        "payment_status": "paid",
                        "shipping_address": {
                          "street": "12 King St",
                          "city": "Sydney",
                          "state": "NSW",
                          "zip": "2000",
                          "country": "AU"
                        },
                        "po_number": null,
                        "status": "pending",
                        "order_type": "retail_hosted",
                        "customer_data": {
                          "email": "buyer@example.com",
                          "name": "Sam Buyer",
                          "phone": "+61400000000"
                        },
                        "items": [
                          {
                            "product_id": 8812,
                            "product_name": "Oak side table",
                            "sku": "OAK-ST-01",
                            "quantity": 1,
                            "price_cents": 12990,
                            "total_cents": 12990
                          }
                        ],
                        "shipping_data": null,
                        "notes": null,
                        "created_at": "2026-08-28T02:10:00.000Z",
                        "updated_at": "2026-08-28T02:10:00.000Z",
                        "fulfilled_at": null,
                        "shipped_at": null,
                        "delivered_at": null
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the delivery (body ignored)."
          },
          "410": {
            "description": "Gone: permanently unsubscribe this endpoint."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "A vendor API key (`btab_...`) from Settings \u2192 API keys, OR an OAuth 2.1 access token. Both are scoped `read` (GET/HEAD only) or `read_write` (everything)."
      },
      "oauth2": {
        "type": "oauth2",
        "description": "Authorization code with PKCE (S256), public clients only. The access token is sent as a bearer credential.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://api.btab.app/oauth/authorize",
            "tokenUrl": "https://api.btab.app/oauth/token",
            "refreshUrl": "https://api.btab.app/oauth/token",
            "scopes": {
              "read": "Read-only: GET/HEAD requests.",
              "read_write": "Full access to the store.",
              "read_products": "Partner apps: read the catalogue and the store's listings.",
              "write_products": "Partner apps: add, price and remove the store's listings (implies read_products).",
              "read_inventory": "Partner apps: read stock levels.",
              "write_inventory": "Partner apps: change stock levels (implies read_inventory).",
              "read_orders": "Partner apps: read orders, without buyer details.",
              "write_orders": "Partner apps: create and update orders (implies read_orders).",
              "read_customers": "Partner apps: buyer names, emails and addresses on orders. Its own opt-in; never implied.",
              "write_external_orders": "Partner apps: create orders that came from outside btab (a marketplace). Its own opt-in; not implied by write_orders."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The error body. The global handler (src/middleware/errorHandler.js) always sends `{ error, message }` where `error` is a status category (\"Bad Request\", \"Not Found\", ...) and `message` is the human-readable text. Some older handlers send only `error`, carrying the human message itself; clients should show `message` when present, else `error`. `code` is a stable machine-readable code when one exists (e.g. RETAIL_PRICE_LOCKED, CUSTOM_PRICING_TIER_LOCKED). Extra keys may appear.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "Not Found"
            ]
          },
          "message": {
            "type": "string",
            "examples": [
              "Order not found"
            ]
          },
          "code": {
            "type": "string"
          },
          "validation_errors": {
            "type": "array",
            "description": "Present on request-validation failures (400).",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "field",
                "message"
              ]
            }
          }
        },
        "additionalProperties": true
      },
      "ReadOnlyScopeError": {
        "description": "Sent by enforceApiKeyScope when a `read` credential attempts a non-GET/HEAD request.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "scope": {
                "type": "string",
                "examples": [
                  "read"
                ]
              },
              "required_scope": {
                "type": "string",
                "const": "read_write"
              }
            }
          }
        ]
      },
      "RateLimitError": {
        "type": "object",
        "required": [
          "error",
          "message",
          "retry_after_seconds"
        ],
        "properties": {
          "error": {
            "type": "string",
            "const": "Rate Limit Exceeded"
          },
          "message": {
            "type": "string"
          },
          "retry_after_seconds": {
            "type": "integer"
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "description": "RFC 6749 \u00a75.2 error body, used by every /oauth endpoint.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "invalid_request",
              "invalid_grant",
              "invalid_client",
              "invalid_redirect_uri",
              "invalid_client_metadata",
              "unsupported_grant_type",
              "invalid_target",
              "not_found",
              "temporarily_unavailable"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "required": [
          "page",
          "limit",
          "total_count",
          "total_pages"
        ],
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total_count": {
            "type": "integer"
          },
          "total_pages": {
            "type": "integer"
          }
        }
      },
      "Product": {
        "type": "object",
        "description": "A catalogue product as a vendor sees it. Built from an ALLOWLIST (productController.VENDOR_PRODUCT_FIELDS via sanitizeProductForVendor) \u2014 supplier cost and BTAB margin fields never appear. A key is present only when the underlying row carries it. `wholesale_price_cents` is omitted when the platform's wholesale-disclosure setting is off.",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "description_format": {
            "type": [
              "string",
              "null"
            ],
            "description": "How `description` is encoded (e.g. `html`, `text`)."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "product_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Category (raw taxonomy value)."
          },
          "seo_title": {
            "type": [
              "string",
              "null"
            ]
          },
          "seo_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Primary image, medium size (signed URL)."
          },
          "wholesale_price_cents": {
            "type": "integer",
            "description": "The vendor's buy price. Omitted when wholesale disclosure is off."
          },
          "retail_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Recommended retail price (RRP)."
          },
          "in_stock": {
            "type": "boolean",
            "description": "Whether the product can currently be sold."
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Units available across the product (null = not tracked)."
          },
          "has_variants": {
            "type": "boolean"
          },
          "published": {
            "type": "boolean"
          },
          "pricing_type": {
            "type": "string",
            "enum": [
              "wholesale",
              "commission"
            ]
          },
          "commission_percent": {
            "type": [
              "number",
              "null"
            ],
            "description": "For `commission` products, the VENDOR's share of the selling price (not the supplier's full rate)."
          },
          "supplier_retail_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Commission products only: the fixed selling price the vendor sells at."
          },
          "supplier_id": {
            "type": "integer"
          },
          "supplier_business_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "approval_status": {
            "type": "string",
            "examples": [
              "approved"
            ]
          },
          "enquiry_only": {
            "type": "boolean",
            "description": "Inherited selling mode: true = buyers enquire for a quote instead of paying by card."
          },
          "brand": {
            "type": [
              "string",
              "null"
            ]
          },
          "readiness_band": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ready",
              "fair",
              "poor",
              null
            ]
          },
          "image_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "price_floor_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The minimum custom retail price the API will accept for this product."
          },
          "price_floor_mode": {
            "type": "string",
            "enum": [
              "breakeven",
              "wholesale",
              "off"
            ]
          },
          "price_floor_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human sentence describing the floor; null when the floor is off."
          },
          "vendor_earn_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What this store is paid for one sale at `retail_price_cents` (the checkout split, not retail \u2212 wholesale)."
          }
        },
        "additionalProperties": false
      },
      "ProductImage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "image_key": {
            "type": "string"
          },
          "alt_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_primary": {
            "type": "boolean"
          },
          "position": {
            "type": "integer"
          },
          "urls": {
            "$ref": "#/components/schemas/ImageUrls"
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Medium size."
          }
        }
      },
      "ImageUrls": {
        "type": [
          "object",
          "null"
        ],
        "description": "Signed URLs per size.",
        "properties": {
          "thumbnail": {
            "type": "string",
            "format": "uri"
          },
          "small": {
            "type": "string",
            "format": "uri"
          },
          "medium": {
            "type": "string",
            "format": "uri"
          },
          "large": {
            "type": "string",
            "format": "uri"
          },
          "original": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ProductOption": {
        "type": "object",
        "description": "A product option axis (e.g. Size). Raw product_options row.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "product_id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "position": {
            "type": "integer"
          },
          "option_values": {
            "type": "array",
            "items": {},
            "description": "The values of this option, in order."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "ProductVariant": {
        "type": "object",
        "description": "A purchasable unit, allowlisted by src/lib/variantPublic.js. `price_cents` is the product's effective retail (per-variant pricing does not exist yet); the raw import price column never leaves the API. Stock is per variant: `in_stock` + `stock_quantity`.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "product_id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ]
          },
          "price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Display/sell price = the product's effective retail."
          },
          "compare_at_price_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "weight_grams": {
            "type": [
              "number",
              "null"
            ]
          },
          "weight_unit": {
            "type": [
              "string",
              "null"
            ]
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "in_stock": {
            "type": "boolean"
          },
          "option1_value": {
            "type": [
              "string",
              "null"
            ]
          },
          "option2_value": {
            "type": [
              "string",
              "null"
            ]
          },
          "option3_value": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_id": {
            "type": [
              "integer",
              "null"
            ]
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "length_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Carton (parcel) length."
          },
          "width_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Carton (parcel) width."
          },
          "height_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Carton (parcel) height."
          },
          "product_width_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Assembled product width."
          },
          "product_depth_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Assembled product depth."
          },
          "product_height_mm": {
            "type": [
              "number",
              "null"
            ],
            "description": "Assembled product height."
          },
          "package_length_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "package_width_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "package_height_mm": {
            "type": [
              "number",
              "null"
            ]
          },
          "package_weight_grams": {
            "type": [
              "number",
              "null"
            ]
          },
          "package_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "ProductDimensions": {
        "type": [
          "object",
          "null"
        ],
        "description": "Assembled size the buyer's product page shows (never the carton). Null when unknown.",
        "properties": {
          "overall": {
            "type": "object",
            "properties": {
              "w": {
                "type": "number"
              },
              "d": {
                "type": "number"
              },
              "h": {
                "type": "number"
              }
            }
          },
          "named": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Model-measured named sub-dimensions, only when verified."
          },
          "units": {
            "type": "string",
            "const": "mm"
          },
          "source": {
            "type": "string",
            "enum": [
              "supplier",
              "supplier_verified"
            ]
          }
        }
      },
      "ProductDrawing": {
        "type": "object",
        "properties": {
          "image_type": {
            "type": "string"
          },
          "view": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "urls": {
            "type": "object",
            "properties": {
              "thumbnail": {
                "type": "string",
                "format": "uri"
              },
              "large": {
                "type": "string",
                "format": "uri"
              },
              "original": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ProductDetail": {
        "type": "object",
        "required": [
          "product",
          "images",
          "options",
          "variants"
        ],
        "properties": {
          "product": {
            "$ref": "#/components/schemas/Product"
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductImage"
            }
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductOption"
            }
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductVariant"
            }
          },
          "dimensions": {
            "$ref": "#/components/schemas/ProductDimensions"
          },
          "drawings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductDrawing"
            }
          }
        }
      },
      "ProductList": {
        "type": "object",
        "required": [
          "products",
          "pagination"
        ],
        "properties": {
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          },
          "search": {
            "type": "object",
            "description": "Present only when `search` was given. `fallback: true` = full-text found nothing and a substring match answered instead.",
            "properties": {
              "term": {
                "type": "string"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "fts",
                  "ilike"
                ]
              },
              "fallback": {
                "type": "boolean"
              }
            }
          },
          "sort": {
            "type": "object",
            "description": "What ordered this page.",
            "properties": {
              "applied": {
                "type": "string"
              },
              "default": {
                "type": "string"
              },
              "niche": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "scope": {
            "type": "object",
            "description": "What the page was scoped to (store types). `applied: all` with `requested: null` is the catalogue as before. `outside_count` = matches outside the store's departments, computed when scoped and searching (or scoped to nothing); else null.",
            "properties": {
              "applied": {
                "type": "string",
                "enum": [
                  "store",
                  "all"
                ]
              },
              "requested": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "default": {
                "type": "string",
                "enum": [
                  "store",
                  "all"
                ]
              },
              "departments": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string"
                }
              },
              "labels": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    }
                  }
                }
              },
              "source": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "outside_count": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          },
          "ownership": {
            "type": "object",
            "description": "Present only when the request sent `ownership` and the store's account has its own suppliers. `applied` is the part this page holds; `choices` are the values the store may send. Absent for every other store and request.",
            "properties": {
              "applied": {
                "type": "string",
                "enum": [
                  "own",
                  "btab",
                  "all"
                ]
              },
              "choices": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "own",
                    "btab",
                    "all"
                  ]
                }
              }
            }
          }
        }
      },
      "CatalogFacets": {
        "type": "object",
        "description": "Value + count lists for the catalogue filters, computed over the same filter set as GET /api/v1/products. Shape kept loose; see productModel.getCatalogFacets.",
        "properties": {
          "own_suppliers": {
            "type": "boolean",
            "description": "Whether this store's account has a supplier of its own. Asked of the account, not read from `suppliers` (that list is capped and follows the filters). Always `false` for a store btab sells for."
          },
          "suppliers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "name": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "brands": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "category_groups": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "store_types": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "purchase": {
            "type": "object",
            "properties": {
              "buy_now": {
                "type": "integer"
              },
              "enquiry_only": {
                "type": "integer"
              }
            }
          },
          "readiness": {
            "type": "object",
            "properties": {
              "ready": {
                "type": "integer"
              },
              "not_ready": {
                "type": "integer"
              }
            }
          },
          "completeness": {
            "type": "array",
            "description": "Per computed channel, products 100% complete for it under the other filters (send value back as complete_for).",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "complete": {
                  "type": "integer"
                }
              }
            }
          },
          "price_bands": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "min_cents": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "max_cents": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "truncated": {
            "type": "object",
            "properties": {
              "suppliers": {
                "type": "boolean"
              },
              "brands": {
                "type": "boolean"
              }
            }
          },
          "facet_limit": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "VendorProfile": {
        "type": "object",
        "required": [
          "vendor",
          "statistics"
        ],
        "properties": {
          "vendor": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "is_active": {
                "type": "boolean"
              },
              "metadata": {
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": true
              },
              "tier": {
                "type": "string",
                "examples": [
                  "free",
                  "pro"
                ]
              },
              "store_type": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The store's niche (a BTAB store_type) or null."
              },
              "what_to_sell": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "statistics": {
            "type": "object",
            "properties": {
              "total_orders": {
                "type": "integer"
              },
              "pending_orders": {
                "type": "integer"
              },
              "fulfilled_orders": {
                "type": "integer"
              },
              "total_revenue_cents": {
                "type": "integer"
              }
            }
          }
        }
      },
      "Listing": {
        "type": "object",
        "description": "One product in the vendor's store (a vendor_products row joined to its product). `id` is the PRODUCT id; `vendor_product_id` is the listing row.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Product id."
          },
          "vendor_product_id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "Custom title if set, else the catalogue name."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "description_format": {
            "type": [
              "string",
              "null"
            ]
          },
          "original_name": {
            "type": "string"
          },
          "original_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "original_description_format": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_title": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_description_format": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_content_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "seo_keywords": {},
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "wholesale_price_cents": {
            "type": "integer",
            "description": "Omitted when wholesale disclosure is off."
          },
          "retail_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Effective selling price: custom price if set, else catalogue retail."
          },
          "vendor_earn_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What this store is paid for one sale at that price."
          },
          "custom_pricing": {
            "type": "boolean",
            "description": "True when a custom retail price is set."
          },
          "in_stock": {
            "type": "boolean"
          },
          "stock_quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "vendor_fulfilled": {
            "type": "boolean",
            "description": "True when the store ships this listing itself. Not the same as `own_goods`."
          },
          "enquiry_only_override": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Listing-level override; null = inherit."
          },
          "product_enquiry_only": {
            "type": "boolean"
          },
          "effective_enquiry_only": {
            "type": "boolean"
          },
          "channel_visibility": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            },
            "description": "Per-channel feed visibility, e.g. `{ \"google\": false }`."
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "added_at": {
            "type": "string",
            "format": "date-time"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "price_floor_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "price_floor_mode": {
            "type": "string",
            "enum": [
              "breakeven",
              "wholesale",
              "off"
            ]
          },
          "price_floor_label": {
            "type": [
              "string",
              "null"
            ]
          },
          "own_goods": {
            "type": "boolean",
            "description": "True when this is the store's OWN product (its maker is the store's own private supplier). The same rule an external order holds by: only own goods can be sold through a marketplace without a hold. Not the same as `vendor_fulfilled`."
          },
          "category": {
            "$ref": "#/components/schemas/ListingCategory"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Every photo the store shows for this listing, in its order (large size). Empty when the store shows none."
          },
          "brand": {
            "type": [
              "string",
              "null"
            ],
            "description": "The product's brand, when set."
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "The product's barcode (GTIN/EAN/UPC): its own, else its one active variant's. Null for a product with several variants: use each variant's."
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The option group title, e.g. Colour."
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            },
            "description": "Only with `include=variants`."
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ListingVariantSummary"
            },
            "description": "Only with `include=variants`."
          }
        }
      },
      "ListingList": {
        "type": "object",
        "required": [
          "products",
          "pagination"
        ],
        "properties": {
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Listing"
            }
          },
          "total_count": {
            "type": "integer"
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        }
      },
      "Money": {
        "type": "object",
        "required": [
          "amount",
          "currency"
        ],
        "properties": {
          "amount": {
            "type": "integer",
            "description": "Minor units (cents)."
          },
          "currency": {
            "type": "string",
            "examples": [
              "AUD"
            ]
          }
        }
      },
      "ListingVariant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "compare_at_price": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Money"
              },
              {
                "type": "null"
              }
            ]
          },
          "available_for_sale": {
            "type": "boolean"
          },
          "quantity_available": {
            "type": [
              "integer",
              "null"
            ]
          },
          "selected_options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "weight": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "value": {
                "type": "number"
              },
              "unit": {
                "type": "string"
              }
            }
          },
          "product_size": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "w": {
                "type": "number"
              },
              "d": {
                "type": "number"
              },
              "h": {
                "type": "number"
              },
              "units": {
                "type": "string",
                "const": "mm"
              }
            }
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "ListingDetail": {
        "type": "object",
        "required": [
          "product"
        ],
        "properties": {
          "product": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "description": "Product id."
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "description_format": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "sku": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "available_for_sale": {
                "type": "boolean"
              },
              "price_range": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "$ref": "#/components/schemas/Money"
                  },
                  "max": {
                    "$ref": "#/components/schemas/Money"
                  }
                }
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "values": {
                      "type": "array",
                      "items": {}
                    }
                  }
                }
              },
              "variants": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ListingVariant"
                }
              },
              "product_size": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "w": {
                    "type": "number"
                  },
                  "d": {
                    "type": "number"
                  },
                  "h": {
                    "type": "number"
                  },
                  "units": {
                    "type": "string",
                    "const": "mm"
                  },
                  "source": {
                    "type": "string"
                  }
                }
              },
              "drawings": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ProductDrawing"
                }
              },
              "retail_price_cents": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "vendor_earn_cents": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "custom_pricing": {
                "type": "boolean"
              },
              "in_stock": {
                "type": "boolean"
              },
              "stock_quantity": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "vendor_fulfilled": {
                "type": "boolean",
                "description": "True when the store ships this listing itself. Not the same as `own_goods`."
              },
              "image_url": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uri"
              },
              "added_at": {
                "type": "string",
                "format": "date-time"
              },
              "own_goods": {
                "type": "boolean",
                "description": "True when this is the store's OWN product (its maker is the store's own private supplier). The same rule an external order holds by: only own goods can be sold through a marketplace without a hold. Not the same as `vendor_fulfilled`."
              },
              "category": {
                "$ref": "#/components/schemas/ListingCategory"
              },
              "images": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uri"
                },
                "description": "Every photo the store shows for this listing, in its order (large size). Empty when the store shows none."
              },
              "brand": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The product's brand, when set."
              },
              "barcode": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The product's barcode (GTIN/EAN/UPC): its own, else its one active variant's. Null for a product with several variants: use each variant's."
              }
            }
          }
        }
      },
      "AddListingRequest": {
        "type": "object",
        "required": [
          "product_id"
        ],
        "properties": {
          "product_id": {
            "type": "integer",
            "description": "An approved, active catalogue product with images."
          },
          "custom_retail_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Optional custom retail. Pro tier only (403 CUSTOM_PRICING_TIER_LOCKED otherwise); refused on commission products (403 RETAIL_PRICE_LOCKED); must be \u2265 the price floor (400)."
          }
        }
      },
      "AddListingResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "vendor_product": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "product_id": {
                "type": "integer"
              },
              "custom_retail_price_cents": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "added_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "CatalogFilters": {
        "type": "object",
        "description": "The catalogue filter keys (productModel.CATALOG_FILTER_KEYS); same meaning as the GET /api/v1/products query parameters. `{}` in bulk-remove means the whole store.",
        "properties": {
          "in_stock": {
            "type": "boolean"
          },
          "search": {
            "type": "string"
          },
          "pricing_type": {
            "type": "string",
            "enum": [
              "wholesale",
              "commission"
            ]
          },
          "supplier_id": {
            "type": "integer"
          },
          "brand": {
            "type": "string"
          },
          "category": {
            "type": "string"
          },
          "store_type": {
            "type": "string"
          },
          "exclude_in_store": {
            "type": "boolean"
          },
          "purchase_mode": {
            "type": "string",
            "enum": [
              "buy_now",
              "enquiry"
            ]
          },
          "price_min_cents": {
            "type": "integer"
          },
          "price_max_cents": {
            "type": "integer"
          },
          "ready_to_list": {
            "type": "boolean"
          },
          "complete_for": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "store",
              "all"
            ],
            "description": "Same as the `scope` query parameter: bulk add / clear by filter act on exactly the rows the scoped grid shows. Omit for the platform default."
          },
          "ownership": {
            "type": "string",
            "enum": [
              "own",
              "btab",
              "all"
            ],
            "description": "Same as the `ownership` query parameter: bulk add / clear by filter act on exactly the part the grid shows (the store's own suppliers' products, the rest, or both). Ignored for a store without own suppliers."
          }
        },
        "additionalProperties": false
      },
      "BulkListingRequest": {
        "type": "object",
        "required": [
          "mode"
        ],
        "description": "`mode: \"ids\"` with `product_ids` (1\u2013500 positive integers), or `mode: \"filter\"` with `filters`.",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "ids",
              "filter"
            ]
          },
          "product_ids": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1
            },
            "minItems": 1,
            "maxItems": 500
          },
          "filters": {
            "$ref": "#/components/schemas/CatalogFilters"
          }
        }
      },
      "BulkAddResult": {
        "type": "object",
        "properties": {
          "added": {
            "type": "integer"
          },
          "skipped": {
            "type": "array",
            "description": "ids mode only.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "reason": {
                  "type": "string",
                  "enum": [
                    "not_found",
                    "not_approved",
                    "discontinued",
                    "no_images"
                  ]
                }
              }
            }
          },
          "requested": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string",
                "const": "filter"
              }
            ]
          }
        }
      },
      "BulkRemoveResult": {
        "type": "object",
        "properties": {
          "removed": {
            "type": "integer"
          },
          "requested": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "string",
                "const": "filter"
              }
            ]
          }
        }
      },
      "PricingUpdateRequest": {
        "type": "object",
        "required": [
          "custom_retail_price_cents"
        ],
        "properties": {
          "custom_retail_price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "`null` clears the override (allowed on every tier). A number sets it: Pro tier only, refused on commission products, and must be \u2265 the price floor."
          }
        }
      },
      "PricingUpdateResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "vendor_product": {
            "type": "object",
            "properties": {
              "product_id": {
                "type": "integer"
              },
              "custom_retail_price_cents": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        }
      },
      "EnquiryModeRequest": {
        "type": "object",
        "required": [
          "enquiry_only_override"
        ],
        "properties": {
          "enquiry_only_override": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "`true` = enquiry only, `false` = card checkout (only allowed on vendor-fulfilled listings, else 409), `null` = inherit the product setting."
          }
        }
      },
      "EnquiryModeResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "product_id": {
            "type": "integer"
          },
          "enquiry_only_override": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      },
      "VendorFulfilledRequest": {
        "type": "object",
        "required": [
          "vendor_fulfilled"
        ],
        "properties": {
          "vendor_fulfilled": {
            "type": "boolean"
          }
        }
      },
      "VendorFulfilledResponse": {
        "type": "object",
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "vendor_fulfilled": {
            "type": "boolean"
          }
        }
      },
      "MessageResponse": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "OrderItem": {
        "type": "object",
        "description": "One line of `orders.items` (JSONB). The listed keys are what API-created (wholesale_api) orders carry; storefront and invoice orders may carry more (e.g. variant or image keys), so the schema is deliberately open.",
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "product_name": {
            "type": "string"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "integer",
            "minimum": 1
          },
          "price_cents": {
            "type": "integer",
            "description": "Unit price."
          },
          "total_cents": {
            "type": "integer",
            "description": "quantity \u00d7 price_cents."
          }
        },
        "additionalProperties": true
      },
      "Address": {
        "type": "object",
        "required": [
          "street",
          "city",
          "state",
          "zip",
          "country"
        ],
        "properties": {
          "street": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        }
      },
      "Customer": {
        "type": "object",
        "required": [
          "email",
          "name",
          "address"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          }
        },
        "additionalProperties": true
      },
      "InventoryVariant": {
        "type": "object",
        "properties": {
          "variant_id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "external_skus": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The store's SKU aliases for this option (e.g. its marketplace SKUs)."
          },
          "active": {
            "type": "boolean"
          },
          "available": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Units available to sell now (already net of open orders). null when the store does not track this stock."
          },
          "tracked": {
            "type": "boolean"
          },
          "in_stock": {
            "type": "boolean"
          }
        }
      },
      "InventoryItem": {
        "type": "object",
        "properties": {
          "product_id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "external_skus": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "listed": {
            "type": "boolean",
            "description": "false = the store switched this listing off: sell none of it on the marketplace."
          },
          "sellable": {
            "type": "boolean",
            "description": "false = the store may not sell this on a marketplace through the app yet (it is btab-catalogue stock, not the store's own goods; such orders are held until btab can collect wholesale on them). It then reports in_stock false and available 0 for the product and every option, so the app takes it off the marketplace."
          },
          "available": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Units available to sell now (already net of open orders). null when the store does not track this stock."
          },
          "tracked": {
            "type": "boolean"
          },
          "in_stock": {
            "type": "boolean"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InventoryVariant"
            },
            "description": "The options, for a product sold by option; empty otherwise."
          }
        }
      },
      "InventoryPage": {
        "type": "object",
        "required": [
          "items",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InventoryItem"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Send it back as updated_since to get what changed after this page. Once a read reaches the end of the data, next_cursor starts a short window back (external_inventory_cursor_lag_seconds, default 120), so a change that committed late is read again rather than skipped: expect to see recently changed items again, and compare."
          }
        }
      },
      "ExternalOrderLine": {
        "type": "object",
        "required": [
          "sku",
          "quantity",
          "unit_price_cents"
        ],
        "properties": {
          "sku": {
            "type": "string",
            "description": "The SKU as the marketplace knows it. Resolved to the store's product through its SKU aliases, then its listings' SKUs."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1
          },
          "unit_price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "What the buyer paid per unit, in AUD cents including GST. A line at 0 is held."
          },
          "title": {
            "type": "string"
          }
        }
      },
      "ExternalOrderRequest": {
        "type": "object",
        "required": [
          "source",
          "account_id",
          "order_id",
          "lines"
        ],
        "properties": {
          "source": {
            "type": "string",
            "description": "The marketplace, e.g. \"kogan\". Must be one of the platform's allowed sources."
          },
          "account_id": {
            "type": "string",
            "description": "The store's seller id on that marketplace (Kogan SellerID)."
          },
          "order_id": {
            "type": "string",
            "description": "The marketplace's order id. Posting the same id again returns the same order."
          },
          "order_number": {
            "type": "string"
          },
          "marketplace_ref": {
            "type": "string",
            "description": "The marketplace reference the store's Shopify may already carry for this sale; used to never import it twice."
          },
          "placed_at": {
            "type": "string",
            "format": "date-time"
          },
          "buyer": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "phone": {
                "type": "string"
              }
            }
          },
          "shipping_address": {
            "$ref": "#/components/schemas/Address"
          },
          "shipping_cents": {
            "type": "integer",
            "minimum": 0
          },
          "lines": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/ExternalOrderLine"
            }
          }
        }
      },
      "ExternalOrderResponse": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "created",
              "duplicate",
              "held"
            ]
          },
          "order_id": {
            "type": "integer"
          },
          "matched": {
            "type": "string",
            "enum": [
              "external_id",
              "marketplace_ref"
            ],
            "description": "How a duplicate was recognised."
          },
          "reason": {
            "type": "string",
            "enum": [
              "needs_product",
              "btab_supplied"
            ],
            "description": "Why an order is held."
          },
          "unmatched": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "sku": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "quantity": {
                  "type": "integer"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "An order row. GET /orders/{id} adds `invoice_number`, `invoice_issued_at`, `source_enquiry_id`, `source_quote_number`; the list adds `channel` and `attribution`. `customer_data`, `shipping_address` and `shipping_data` are JSON blobs whose keys vary by order type, so they are left open.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "vendor_id": {
            "type": "integer"
          },
          "order_number": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "fulfilled",
              "shipped",
              "delivered",
              "cancelled"
            ]
          },
          "order_type": {
            "type": "string",
            "enum": [
              "retail_hosted",
              "retail_headless",
              "retail_invoice",
              "wholesale_api"
            ]
          },
          "payment_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "not_required",
              "pending",
              "paid",
              "failed",
              "refunded",
              "disputed",
              null
            ]
          },
          "total_cents": {
            "type": "integer"
          },
          "shipping_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "charge_total_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "tax_amount_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "gift_card_sale_cents": {
            "type": "integer",
            "description": "The part of charge_total_cents that bought gift cards (not goods: no GST, nothing to ship). 0 = none."
          },
          "gift_card_cents": {
            "type": "integer",
            "description": "The part of charge_total_cents a gift card paid (a tender, not a discount). 0 = none."
          },
          "gift_card_sales": {
            "type": "array",
            "description": "Single-order reads only, when gift_card_sale_cents > 0: the gift cards this order bought. The code is never returned; last4 once issued.",
            "items": {
              "type": "object",
              "properties": {
                "line_index": {
                  "type": "integer"
                },
                "amount_cents": {
                  "type": "integer"
                },
                "recipient_email": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "recipient_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "review",
                    "issued",
                    "voided",
                    "cancelled",
                    "void_pending"
                  ]
                },
                "last4": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "gift_card_payments": {
            "type": "array",
            "description": "Single-order reads only, when gift_card_cents > 0: the gift cards that paid part of this order.",
            "items": {
              "type": "object",
              "properties": {
                "amount_cents": {
                  "type": "integer"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "held",
                    "captured",
                    "released",
                    "refunded"
                  ]
                },
                "last4": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OrderItem"
            }
          },
          "customer_data": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "shipping_address": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "shipping_data": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "po_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "fulfilled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "shipped_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tax invoice number; issued when the order ships."
          },
          "invoice_issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "source_enquiry_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The sales quote this order was converted from, if any."
          },
          "source_quote_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "facebook_instagram",
              "google",
              "vendor_link",
              "email",
              "other_referral",
              "direct",
              null
            ],
            "description": "Where the buyer came from; null = placed before attribution existed."
          },
          "attribution": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "OrderList": {
        "type": "object",
        "required": [
          "orders",
          "pagination"
        ],
        "properties": {
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Order"
            }
          },
          "pagination": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Pagination"
              },
              {
                "type": "object",
                "properties": {
                  "status_counts": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer"
                    },
                    "description": "Per-status totals over the whole filtered set (ignoring `status`)."
                  }
                }
              }
            ]
          }
        }
      },
      "CreateOrderRequest": {
        "type": "object",
        "required": [
          "items",
          "customer"
        ],
        "properties": {
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "product_id",
                "quantity"
              ],
              "properties": {
                "product_id": {
                  "type": "integer",
                  "minimum": 1
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1
                }
              }
            }
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "CreateOrderResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "order_number": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "order_type": {
                "type": "string",
                "const": "wholesale_api"
              },
              "total_cents": {
                "type": "integer",
                "description": "Sum of wholesale price \u00d7 quantity."
              },
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/OrderItem"
                }
              },
              "customer": {
                "$ref": "#/components/schemas/Customer"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "OrderResponse": {
        "type": "object",
        "required": [
          "order"
        ],
        "properties": {
          "order": {
            "$ref": "#/components/schemas/Order"
          }
        }
      },
      "OrderMutationResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "order": {
            "$ref": "#/components/schemas/Order"
          }
        }
      },
      "UpdateOrderStatusRequest": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "fulfilled",
              "shipped",
              "delivered",
              "cancelled"
            ]
          },
          "notes": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "Fulfilment": {
        "type": [
          "object",
          "null"
        ],
        "description": "The vendor's own-goods fulfilment part for an order (null when the order has none).",
        "properties": {
          "route": {
            "type": "string",
            "enum": [
              "btab_owned",
              "consignment",
              "dropship",
              "vendor_goods",
              "third_party_3pl"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "picked",
              "shipped",
              "delivered",
              "cancelled"
            ]
          },
          "tracking_carrier": {
            "type": [
              "string",
              "null"
            ]
          },
          "tracking_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "accepted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "shipped_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "FulfilmentResponse": {
        "type": "object",
        "required": [
          "fulfilment"
        ],
        "properties": {
          "fulfilment": {
            "$ref": "#/components/schemas/Fulfilment"
          }
        }
      },
      "FulfilmentActionResponse": {
        "type": "object",
        "properties": {
          "fulfilment": {
            "$ref": "#/components/schemas/Fulfilment"
          },
          "order_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "The new order status if the transition advanced it, else null."
          }
        }
      },
      "ShipRequest": {
        "type": "object",
        "required": [
          "tracking_number"
        ],
        "properties": {
          "tracking_number": {
            "type": "string"
          },
          "tracking_carrier": {
            "type": "string"
          }
        }
      },
      "WebhookTopic": {
        "type": "string",
        "enum": [
          "order.created",
          "order.shipped",
          "order.fulfilled",
          "order.completed",
          "site.published",
          "product.created",
          "product.updated",
          "product.deleted",
          "inventory.updated",
          "*"
        ],
        "description": "`*` = all topics."
      },
      "WebhookSubscription": {
        "type": "object",
        "description": "Never includes the signing secret.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "vendor_id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookTopic"
            }
          },
          "active": {
            "type": "boolean"
          },
          "disabled_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "consecutive_failures": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookSubscriptionList": {
        "type": "object",
        "properties": {
          "subscriptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookSubscription"
            }
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Every topic you can subscribe to."
          },
          "configured": {
            "type": "boolean",
            "description": "False when webhooks are not provisioned on this deployment (create would 503)."
          }
        }
      },
      "WebhookSubscriptionCreateRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "https only, publicly resolvable host."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookTopic"
            }
          }
        }
      },
      "WebhookSubscriptionUpdateRequest": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/WebhookTopic"
            }
          },
          "active": {
            "type": "boolean",
            "description": "`true` re-enables and clears the failure streak."
          }
        }
      },
      "WebhookSubscriptionCreated": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "subscription": {
            "$ref": "#/components/schemas/WebhookSubscription"
          },
          "secret": {
            "type": "string",
            "description": "Signing secret (`whsec_...`). Shown exactly once."
          }
        }
      },
      "WebhookSubscriptionResponse": {
        "type": "object",
        "properties": {
          "subscription": {
            "$ref": "#/components/schemas/WebhookSubscription"
          }
        }
      },
      "WebhookSecretRotated": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "New signing secret. Shown exactly once; signs immediately."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "subscription_id": {
            "type": "integer"
          },
          "event": {
            "type": "string"
          },
          "event_key": {
            "type": "string"
          },
          "attempt": {
            "type": "integer"
          },
          "status_code": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookDeliveryList": {
        "type": "object",
        "properties": {
          "deliveries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookDelivery"
            },
            "description": "Last 100 attempts, newest first."
          }
        }
      },
      "WebhookResendResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "event_key": {
            "type": "string"
          },
          "next_attempt": {
            "type": "integer"
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "The envelope of every delivery (src/services/webhookDispatcher.js).",
        "required": [
          "event",
          "event_key",
          "created_at",
          "data"
        ],
        "properties": {
          "event": {
            "type": "string",
            "description": "The topic."
          },
          "event_key": {
            "type": "string",
            "description": "Idempotency key: `<event>:<entity id>` (catalogue events: `<event>:<product id>.<ms timestamp>`). Dedupe on this."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "OrderEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEvent"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "required": [
                  "order"
                ],
                "properties": {
                  "order": {
                    "$ref": "#/components/schemas/Order"
                  }
                }
              }
            }
          }
        ]
      },
      "SitePublishedEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEvent"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "required": [
                  "site"
                ],
                "properties": {
                  "site": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "slug": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "is_published": {
                        "type": "boolean"
                      },
                      "published_at": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "CatalogEvent": {
        "description": "Coalesced and ids-only: read the current product/stock from the API when it arrives.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEvent"
          },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "required": [
                  "product_id"
                ],
                "properties": {
                  "product_id": {
                    "type": "integer"
                  },
                  "variant_id": {
                    "type": "integer",
                    "description": "Present when one variant changed."
                  }
                }
              }
            }
          }
        ]
      },
      "AuthorizationServerMetadata": {
        "type": "object",
        "description": "RFC 8414.",
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "registration_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "client_id_metadata_document_supported": {
            "type": "boolean"
          },
          "service_documentation": {
            "type": "string",
            "format": "uri"
          },
          "revocation_endpoint": {
            "type": "string",
            "format": "uri",
            "description": "RFC 7009 token revocation (`/oauth/revoke`)."
          },
          "revocation_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "none"
              ]
            }
          }
        }
      },
      "ProtectedResourceMetadata": {
        "type": "object",
        "description": "RFC 9728.",
        "properties": {
          "resource": {
            "type": "string",
            "format": "uri"
          },
          "authorization_servers": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "bearer_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "resource_name": {
            "type": "string"
          },
          "resource_documentation": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ClientRegistrationRequest": {
        "type": "object",
        "required": [
          "redirect_uris"
        ],
        "description": "RFC 7591 dynamic client registration. Only these fields are read; the client is always public (`token_endpoint_auth_method: none`) with PKCE.",
        "properties": {
          "redirect_uris": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "https, or http on loopback only."
          },
          "client_name": {
            "type": "string",
            "maxLength": 200
          },
          "client_uri": {
            "type": "string",
            "format": "uri"
          },
          "logo_uri": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ClientRegistrationResponse": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string",
            "examples": [
              "dcr_0f3a..."
            ]
          },
          "client_name": {
            "type": "string"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "const": "none"
          },
          "registration_access_token": {
            "type": "string"
          },
          "client_id_issued_at": {
            "type": "integer"
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": [
          "grant_type"
        ],
        "description": "`grant_type=authorization_code` needs `code` + `code_verifier` (PKCE S256); `client_id`, `redirect_uri` and `resource` are checked when sent. `grant_type=refresh_token` needs `refresh_token`; refresh tokens rotate, and presenting a rotated one revokes the whole connection.",
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "authorization_code",
              "refresh_token"
            ]
          },
          "code": {
            "type": "string"
          },
          "code_verifier": {
            "type": "string"
          },
          "redirect_uri": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "resource": {
            "type": "string",
            "description": "RFC 8707; must equal this server's resource URL."
          },
          "refresh_token": {
            "type": "string"
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "refresh_token",
          "scope"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "const": "Bearer"
          },
          "expires_in": {
            "type": "integer"
          },
          "refresh_token": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ]
          }
        }
      },
      "ListingCategory": {
        "type": [
          "object",
          "null"
        ],
        "description": "The product's category, or null when it has none. `path` is the product's own category path (most general first); `slug`/`name` are btab's category for it, null when that path maps to none.",
        "required": [
          "slug",
          "name",
          "path"
        ],
        "properties": {
          "slug": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ListingVariantSummary": {
        "type": "object",
        "description": "A variant of a listing (with `include=variants`): its identity, stock, own photo and the price a shopper is charged for it (the same price as on GET /api/v1/my-products/{productId}).",
        "properties": {
          "id": {
            "type": "integer"
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "barcode": {
            "type": [
              "string",
              "null"
            ]
          },
          "selected_options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "available_for_sale": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "quantity_available": {
            "type": [
              "integer",
              "null"
            ]
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "hidden": {
            "type": "boolean",
            "description": "True when this option is no longer for sale."
          },
          "price": {
            "$ref": "#/components/schemas/Money"
          },
          "price_source": {
            "type": "string",
            "enum": [
              "own",
              "product"
            ],
            "description": "Only on a store with its own per-option prices: `own` = this option's own price, `product` = the listing's."
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "This option's own photo, when it has one."
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request (validation failures carry `validation_errors`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, invalid, revoked or expired credential. When OAuth is enabled the response carries a `WWW-Authenticate: Bearer resource_metadata=\"...\"` challenge.",
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            },
            "description": "Present when OAuth is enabled."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The credential is `read`-scoped (read-only) and this is a write, the vendor account is inactive, or a feature/tier gate refused the action (`code` says which).",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ReadOnlyScopeError"
                },
                {
                  "$ref": "#/components/schemas/Error"
                }
              ]
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or not yours (other vendors' resources are indistinguishable from missing ones).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Conflict with current state.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitError"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Feature not configured on this deployment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "OAuthError": {
        "description": "OAuth error (RFC 6749 \u00a75.2).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OAuthError"
            }
          }
        }
      },
      "OAuthNotEnabled": {
        "description": "OAuth is not enabled on this server (`error: not_found`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OAuthError"
            }
          }
        }
      }
    }
  },
  "x-btab-planned": [
    {
      "what": "Stock adjust for a listing (the read is GET /api/v1/inventory)",
      "status": "designing",
      "note": "Read: GET /api/v1/inventory (with a change cursor) and the inventory.updated webhook. Adjusting stock from outside is still being designed."
    }
  ]
}
