{
  "openapi": "3.1.0",
  "info": {
    "title": "CopperSmith SKU API",
    "version": "1.0.0",
    "description": "Read-only public API for CopperSmith lantern SKU resolution. Phase 1 (current): static catalog endpoints (the JSON files served from Cloudflare Pages edge). Phase 2: dynamic endpoints (validate, resolve, build, search) shipped as a Cloudflare Worker. See /api/docs for the interactive docs and /llms.txt for the rules reference.",
    "contact": {
      "name": "JAM Group Studio",
      "email": "tech@thecoppersmith.net"
    },
    "license": {
      "name": "Public read-only",
      "identifier": "CC-BY-4.0"
    }
  },
  "servers": [
    {
      "url": "https://sku.thecoppersmith.net/api/v1",
      "description": "Production (custom domain — DNS pending Tuesday)"
    },
    {
      "url": "https://copper-sku.pages.dev/api/v1",
      "description": "Cloudflare Pages preview"
    }
  ],
  "tags": [
    {
      "name": "Catalog (static)",
      "description": "Pre-generated JSON files served from the edge cache. Cache-Control: public, max-age=300, s-maxage=31536000, stale-while-revalidate=86400."
    },
    {
      "name": "SKU resolution (dynamic)",
      "description": "Phase 2 endpoints — shipped as a Cloudflare Worker that imports the same TypeScript engine the UI uses. Not yet live."
    },
    {
      "name": "Search",
      "description": "Fuzzy search over families, base SKUs, accessories. Phase 2."
    }
  ],
  "paths": {
    "/index.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "API root document",
        "description": "Lists every endpoint with a short label. The entry point for AI agents discovering the API.",
        "responses": {
          "200": {
            "description": "API root",
            "content": {
              "application/json": {
                "example": {
                  "name": "CopperSmith SKU API",
                  "version": "1.0.0",
                  "ruleSetVersion": "v4",
                  "masterSheetDate": "2026-05-22",
                  "builtAt": "2026-05-22T18:55:38.019Z",
                  "links": {
                    "self": "/api/v1/index.json",
                    "version": "/api/v1/version.json",
                    "families": "/api/v1/families.json",
                    "family": "/api/v1/families/{familyKey}.json",
                    "baseSkus": "/api/v1/base-skus.json",
                    "baseSku": "/api/v1/base-skus/{sku}.json",
                    "taxonomy": "/api/v1/taxonomy.json",
                    "finishes": "/api/v1/finishes.json",
                    "validate": "/api/v1/validate",
                    "resolve": "/api/v1/resolve",
                    "build": "/api/v1/build",
                    "search": "/api/v1/search",
                    "openapi": "/api/v1/openapi.json",
                    "docs": "/api/docs"
                  },
                  "notes": {
                    "phase": "Phase 1 (static layer) — validate/resolve/build/search are listed for forward-compat and ship in Phase 2 as a Cloudflare Worker.",
                    "cache": "public, max-age=300, s-maxage=31536000, stale-while-revalidate=86400",
                    "auth": "none — fully public read-only catalog data"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/version.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Build provenance",
        "description": "Master sheet date, build timestamp, rule set version, counts, git rev. PIM sync jobs hit this first to decide whether to re-pull.",
        "responses": {
          "200": {
            "description": "Version block",
            "content": {
              "application/json": {
                "example": {
                  "masterSheetDate": "2026-05-22",
                  "builtAt": "2026-05-22T18:55:38.019Z",
                  "ruleSetVersion": "v4",
                  "families": 91,
                  "baseSkus": 283,
                  "gitRev": "dev"
                }
              }
            }
          }
        }
      }
    },
    "/families.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Family index (slim)",
        "description": "Every family with its label, ignition, fixture, base SKU count, and per-family + base-SKU links. No accessory grid — fetch /families/{key}.json for that.",
        "responses": {
          "200": {
            "description": "Family index",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/families/{familyKey}.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Full family payload",
        "description": "One family with its full accessory grid and base SKU list. familyKey looks like 'AS-E' or 'AOB-G'.",
        "parameters": [
          {
            "name": "familyKey",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "AS-E"
            },
            "description": "Family key (e.g. AS-E, AOB-G)"
          }
        ],
        "responses": {
          "200": {
            "description": "Family payload",
            "content": {
              "application/json": {}
            }
          },
          "404": {
            "description": "Unknown family key"
          }
        }
      }
    },
    "/base-skus.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Base SKU index (flat)",
        "description": "Every base SKU across every family. Marketplace feed generation and warehouse seed scripts use this.",
        "responses": {
          "200": {
            "description": "Base SKU index",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/base-skus/{sku}.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Per-base-SKU detail",
        "description": "Base SKU detail with the accessory list already resolved for that SKU. Used by Shopify/WooCommerce/BigCommerce product detail pages.",
        "parameters": [
          {
            "name": "sku",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "AS41E"
            },
            "description": "Base SKU (e.g. AS41E, AOB28E)"
          }
        ],
        "responses": {
          "200": {
            "description": "Base SKU payload",
            "content": {
              "application/json": {}
            }
          },
          "404": {
            "description": "Unknown base SKU"
          }
        }
      }
    },
    "/taxonomy.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Rule set + human-readable explanations",
        "description": "Category order, mount mutex, additive categories, companion rules, plus human-readable ruleExplanations. The rules of the game.",
        "responses": {
          "200": {
            "description": "Taxonomy payload",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/finishes.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "Finish definitions",
        "description": "The five finishes plus the brassOnlyFamilies exclusion list and human-readable notes.",
        "responses": {
          "200": {
            "description": "Finish payload",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "Catalog (static)"
        ],
        "summary": "This document",
        "description": "OpenAPI 3.1 spec for the SKU API.",
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/validate": {
      "post": {
        "tags": [
          "SKU resolution (dynamic)"
        ],
        "summary": "Validate a SKU string (Phase 2)",
        "description": "Parse a SKU like 'AS41E-BLK-TS3-LTS' and check it against the rules. Not yet live — ships in Phase 2 as a Cloudflare Worker.",
        "deprecated": false,
        "responses": {
          "503": {
            "description": "Phase 2 endpoint — not yet implemented. See /api/docs for status."
          }
        }
      }
    },
    "/resolve": {
      "post": {
        "tags": [
          "SKU resolution (dynamic)"
        ],
        "summary": "Resolve selections to a canonical SKU (Phase 2)",
        "description": "Submit a configurator selection and get back the SKU string. Not yet live — ships in Phase 2.",
        "responses": {
          "503": {
            "description": "Phase 2 endpoint — not yet implemented."
          }
        }
      }
    },
    "/build": {
      "post": {
        "tags": [
          "SKU resolution (dynamic)"
        ],
        "summary": "Bulk SKU enumeration (Phase 2)",
        "description": "Cartesian product across base SKUs, finishes, and accessory picks. Not yet live — ships in Phase 2.",
        "responses": {
          "503": {
            "description": "Phase 2 endpoint — not yet implemented."
          }
        }
      }
    },
    "/search": {
      "get": {
        "tags": [
          "Search"
        ],
        "summary": "Fuzzy search (Phase 2)",
        "description": "Search families, base SKUs, accessory names. Not yet live — ships in Phase 2.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 50
            }
          }
        ],
        "responses": {
          "503": {
            "description": "Phase 2 endpoint — not yet implemented."
          }
        }
      }
    }
  }
}