{
  "openapi": "3.1.0",
  "info": {
    "title": "Voltaxion B2B Wholesale API",
    "version": "1.2.0",
    "description": "Public catalog and AI-agent integration surface of Voltaxion Limited, a Hong Kong B2B wholesaler of Apple and Beats devices and accessories (other brands sourced on request). Wholesale prices in USD for verified business buyers; payment in full before dispatch; EXW Hong Kong by default with DHL Express shipping; import duties and VAT are paid by the buyer. Minimum order quantity per SKU is in each row (moq, moq_label). Interactive docs for the REST API: https://api.voltaxion.com/docs.",
    "contact": {
      "name": "Voltaxion Trade",
      "email": "hello@voltaxion.com",
      "url": "https://voltaxion.com"
    },
    "license": {
      "name": "Terms of Service",
      "url": "https://voltaxion.com/terms.html"
    }
  },
  "servers": [
    {
      "url": "https://api.voltaxion.com",
      "description": "REST catalog API (Python uvicorn)"
    },
    {
      "url": "https://portal.voltaxion.com",
      "description": "CDN-backed static JSON snapshots"
    },
    {
      "url": "https://mcp.voltaxion.com",
      "description": "MCP server for AI agents (jsonrpc 2.0)"
    }
  ],
  "tags": [
    {
      "name": "catalog",
      "description": "Read-only product catalog (no auth)"
    },
    {
      "name": "ai-discovery",
      "description": "Manifests for ChatGPT / Claude / generic AI agents"
    },
    {
      "name": "quote",
      "description": "Pricing + quote generation (no inventory reservation)"
    },
    {
      "name": "orders",
      "description": "Customer order placement"
    },
    {
      "name": "mcp",
      "description": "Model Context Protocol — tools available to AI agents"
    },
    {
      "name": "ops",
      "description": "Operational health checks"
    }
  ],
  "paths": {
    "/v1/catalog": {
      "get": {
        "tags": [
          "catalog"
        ],
        "summary": "List catalog SKUs (paged, filterable)",
        "description": "Returns {total, items} with the filters applied server-side. Rate limit about 30 requests per minute per IP; over it you get HTTP 429 or 503, retry after 60 seconds. Data refreshed about every 8 hours.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "description": "category or category_name, e.g. smartphone or iPhone",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "condition",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "Brand new",
                "CPO",
                "Asis+",
                "Asis"
              ]
            }
          },
          {
            "name": "model_contains",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_price_usd",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "max_price_usd",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0
            }
          },
          {
            "name": "min_stock",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sku"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; retry after 60 seconds"
          },
          "503": {
            "description": "Rate limited at the edge; retry after 60 seconds"
          }
        }
      }
    },
    "/v1/skus/{sku}": {
      "get": {
        "tags": [
          "catalog"
        ],
        "summary": "Fetch a single SKU by code",
        "parameters": [
          {
            "name": "sku",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Sku"
                }
              }
            }
          },
          "404": {
            "description": "SKU not found"
          }
        }
      }
    },
    "/v1/categories": {
      "get": {
        "tags": [
          "catalog"
        ],
        "summary": "List distinct category slugs with SKU counts",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/quote": {
      "post": {
        "tags": [
          "quote"
        ],
        "summary": "Programmatic quotes: not available yet (returns 501); use the MCP request_quote tool",
        "responses": {
          "501": {
            "description": "Not available yet; use the MCP request_quote tool for an indicative quote"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "ops"
        ],
        "summary": "Liveness probe (alias of /healthz)",
        "responses": {
          "200": {
            "description": "Server alive + catalog freshness"
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "tags": [
          "ops"
        ],
        "summary": "Liveness probe with catalog age + SKU count",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/data/catalog_sample.json": {
      "get": {
        "servers": [
          {
            "url": "https://portal.voltaxion.com"
          }
        ],
        "tags": [
          "catalog"
        ],
        "summary": "Full catalog snapshot (about 2,500 SKUs, JSON array)",
        "description": "Single-file alternative to /v1/catalog for agents that ingest the whole catalog and cache it. Refreshed about every 8 hours; poll at most every 5 minutes.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Sku"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/data/product_images.json": {
      "get": {
        "servers": [
          {
            "url": "https://portal.voltaxion.com"
          }
        ],
        "tags": [
          "catalog"
        ],
        "summary": "Map of model name (and optional |color suffix) → product image URL",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/.well-known/ai-plugin.json": {
      "get": {
        "servers": [
          {
            "url": "https://voltaxion.com"
          }
        ],
        "tags": [
          "ai-discovery"
        ],
        "summary": "ChatGPT-style plugin manifest",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "servers": [
          {
            "url": "https://voltaxion.com"
          }
        ],
        "tags": [
          "ai-discovery"
        ],
        "summary": "Plain-text AI-agent integration guide (schema, vocabulary, rate limits, contact)",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "servers": [
          {
            "url": "https://mcp.voltaxion.com"
          }
        ],
        "tags": [
          "mcp"
        ],
        "summary": "MCP endpoint (Streamable HTTP, stateless, no auth) with 5 tools: search_catalog, get_sku, list_categories, get_trading_terms, request_quote",
        "description": "Model Context Protocol over Streamable HTTP: POST JSON-RPC 2.0 with `Accept: application/json, text/event-stream`; no session header needed. request_quote is indicative and reserves nothing. See https://modelcontextprotocol.io for the spec.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/customer/disputes": {
      "post": {
        "servers": [
          {
            "url": "https://admin.voltaxion.com"
          }
        ],
        "tags": [
          "orders"
        ],
        "summary": "Open a B2B dispute (V1 — admin-side full workflow at /admin/disputes)",
        "responses": {
          "201": {
            "description": "Dispute created — returns VD-YYYY-NNNN ID"
          }
        }
      }
    },
    "/api/customer/orders": {
      "post": {
        "servers": [
          {
            "url": "https://admin.voltaxion.com"
          }
        ],
        "tags": [
          "orders"
        ],
        "summary": "Place a new order (authenticated customers)",
        "description": "PLANNED — not yet implemented. Customer programmatic order placement; ops manually confirms payment before fulfillment.",
        "responses": {
          "201": {
            "description": "(Future) Order created in awaiting_payment state"
          },
          "501": {
            "description": "Requires an authenticated customer account"
          }
        }
      }
    },
    "/api/orders/{orderNumber}/proforma.pdf": {
      "get": {
        "servers": [
          {
            "url": "https://admin.voltaxion.com"
          }
        ],
        "tags": [
          "orders"
        ],
        "summary": "Download a proforma invoice PDF",
        "description": "Returns the proforma PDF for an order in awaiting_payment state. Requires HMAC signature (t query param) — the URL is delivered to the customer in the order-create response and email 02a. PDF is generated on demand and cached server-side.",
        "parameters": [
          {
            "name": "orderNumber",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "VTX1988"
            }
          },
          {
            "name": "t",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "description": "HMAC-SHA256 over orderNumber:customerId keyed by PDF_URL_SECRET"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF stream",
            "content": {
              "application/pdf": {}
            }
          },
          "401": {
            "description": "Missing or invalid HMAC token"
          },
          "404": {
            "description": "Order not found"
          }
        }
      }
    },
    "/api/orders/{orderNumber}/invoice.pdf": {
      "get": {
        "servers": [
          {
            "url": "https://admin.voltaxion.com"
          }
        ],
        "tags": [
          "orders"
        ],
        "summary": "Download a final invoice PDF (post-payment)",
        "description": "Returns the PAID invoice PDF. Stamped on the order only after ops moves it to payment_accepted. Same HMAC scheme as proforma.",
        "parameters": [
          {
            "name": "orderNumber",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "t",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF stream",
            "content": {
              "application/pdf": {}
            }
          },
          "401": {
            "description": "Missing or invalid HMAC token"
          },
          "404": {
            "description": "Invoice not yet issued (order not paid)"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Sku": {
        "type": "object",
        "required": [
          "sku",
          "brand",
          "model",
          "price_usd",
          "currency",
          "stock",
          "moq"
        ],
        "properties": {
          "sku": {
            "type": "string",
            "description": "Stable Voltaxion SKU code (e.g. VTX-IP17PRO-256G-COS-NEW-014)"
          },
          "brand": {
            "type": "string",
            "example": "Apple"
          },
          "model": {
            "type": "string",
            "example": "iPhone 17 Pro Max"
          },
          "category": {
            "type": "string",
            "enum": [
              "smartphone",
              "tablet",
              "wearable",
              "computer",
              "accessory",
              "audio",
              "media"
            ]
          },
          "category_name": {
            "type": "string",
            "example": "iPhone"
          },
          "storage": {
            "type": "string",
            "nullable": true,
            "example": "256GB"
          },
          "color": {
            "type": "string",
            "nullable": true,
            "example": "Cosmic Orange (YW4)"
          },
          "condition": {
            "type": "string",
            "enum": [
              "Brand new",
              "CPO",
              "Asis+",
              "Asis"
            ]
          },
          "condition_note": {
            "type": "string",
            "description": "Full human-readable provenance"
          },
          "grade": {
            "type": "string",
            "nullable": true,
            "enum": [
              "A",
              "AB",
              "B",
              "C",
              "D"
            ],
            "description": "Cosmetic grade (refurb only)"
          },
          "sim_spec": {
            "type": "string",
            "nullable": true
          },
          "version": {
            "type": "string",
            "nullable": true,
            "description": "Apple model number with region suffix (devices) or Apple part number (accessories), supplier verbatim"
          },
          "status": {
            "type": "string",
            "nullable": true
          },
          "stock": {
            "type": "integer",
            "minimum": 0
          },
          "moq": {
            "type": "integer",
            "minimum": 1,
            "description": "Minimum order per SKU: new phones and computers 1, new iPad and Apple Watch 5, accessories 10, Asis and Asis+ 20; Used 50 combined across Used SKUs"
          },
          "price_usd": {
            "type": "number",
            "description": "Wholesale unit price in USD for verified business buyers"
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "region": {
            "type": "string",
            "default": "HKG"
          },
          "supplier": {
            "type": "string"
          },
          "images": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          }
        }
      }
    }
  }
}
