{
  "openapi": "3.0.3",
  "info": {
    "title": "WarehouseAudit API",
    "version": "1.0.0",
    "description": "Public website and product endpoints for WarehouseAudit. Browser requests are intended for the same site origin. Trial creation and enquiry endpoints create real records; the interactive Swagger console is disabled on /api-docs."
  },
  "servers": [
    {
      "url": "/",
      "description": "The current WarehouseAudit host"
    }
  ],
  "tags": [
    { "name": "Sample audit", "description": "Bundled demonstration data only." },
    { "name": "Trial", "description": "Register for a limited real-data trial and submit its authorized audit." },
    { "name": "Early access", "description": "Shopify app availability notifications." },
    { "name": "Enquiries", "description": "Managed invoice-review and 3PL provider enquiries." },
    { "name": "Downloads", "description": "Synthetic report and supported example input downloads." }
  ],
  "paths": {
    "/api/audit": {
      "post": {
        "tags": ["Sample audit"],
        "operationId": "runSampleAudit",
        "summary": "Run the bundled synthetic sample",
        "description": "Accepts only sample=true. Real-file uploads are rejected; use the trial workflow for real data.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["sample"],
                "properties": {
                  "sample": { "type": "string", "enum": ["true"], "description": "Must be exactly true." }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Synthetic sample audit result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/trials": {
      "post": {
        "tags": ["Trial"],
        "operationId": "registerTrial",
        "summary": "Request real-data trial access",
        "description": "Creates a trial registration and returns a bearer token. A normalized email can receive one trial; do not expose or log the returned token. Registration requires consent and authority to share the files.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TrialRegistration" }
            }
          }
        },
        "responses": {
          "201": { "description": "Trial registered.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TrialRegistrationResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/trial/audit": {
      "post": {
        "tags": ["Trial"],
        "operationId": "runTrialAudit",
        "summary": "Upload files for a registered trial",
        "description": "Requires the bearer token returned by POST /api/trials. The allowance is limited and can be consumed by a completed audit or unexpected service failure. Upload supported CSV exports and an agreed YAML rate card; examples or bundled rates are not substituted.",
        "security": [{ "trialToken": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["files"],
                "properties": {
                  "files": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 8,
                    "items": { "type": "string", "format": "binary" },
                    "description": "One to eight files; each must be non-empty and at most 20 MiB, with no more than 40 MiB total. Include at least one supported invoice CSV, one dispatch CSV and one YAML rate card."
                  }
                },
                "additionalProperties": false
              },
              "encoding": { "files": { "style": "form", "explode": true } }
            }
          }
        },
        "responses": {
          "200": { "description": "Audit completed or returned an audit result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/early-access": {
      "post": {
        "tags": ["Early access"],
        "operationId": "requestShopifyEarlyAccess",
        "summary": "Request a Shopify app availability notification",
        "description": "Records a request for contact about early access. This does not install the app or grant audit access.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/EarlyAccessRequest" }
            }
          }
        },
        "responses": {
          "201": { "description": "Request recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/invoice-review": {
      "post": {
        "tags": ["Enquiries"],
        "operationId": "requestManagedInvoiceReview",
        "summary": "Request a managed invoice review",
        "description": "Records an enquiry. Scope and fees are agreed before any review starts. Do not send customer data or confidential rates in the enquiry.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ReviewEnquiry" }
            }
          }
        },
        "responses": {
          "201": { "description": "Enquiry recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/provider-enquiry": {
      "post": {
        "tags": ["Enquiries"],
        "operationId": "requestProviderReview",
        "summary": "Request a 3PL provider review",
        "description": "Records an enquiry about assisted pre-invoice review for a 3PL provider.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ProviderEnquiry" }
            }
          }
        },
        "responses": {
          "201": { "description": "Enquiry recorded.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OkResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "503": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/provider-sample/{format}": {
      "get": {
        "tags": ["Downloads"],
        "operationId": "downloadProviderSampleReport",
        "summary": "Download a synthetic provider sample report",
        "parameters": [
          {
            "name": "format",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "enum": ["csv", "html"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Synthetic report file.",
            "content": {
              "text/csv": { "schema": { "type": "string" } },
              "text/html": { "schema": { "type": "string" } }
            }
          },
          "404": { "description": "Unsupported report format." }
        }
      }
    },
    "/api/sample/{name}": {
      "get": {
        "tags": ["Downloads"],
        "operationId": "downloadSupportedExampleFile",
        "summary": "Download a supported synthetic input example",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": ["mintsoft_billing.csv", "royal_mail_oba.csv", "sendcloud_invoice.csv", "shiphero_manifest.csv"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Synthetic CSV example file.",
            "content": { "text/csv": { "schema": { "type": "string", "format": "binary" } } }
          },
          "404": { "description": "Unknown example file." },
          "500": { "description": "Example file is missing from this deployment." }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "trialToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Opaque trial token",
        "description": "Returned by POST /api/trials. Treat it as a secret. It expires after seven days and authorizes one limited trial allowance."
      }
    },
    "schemas": {
      "OkResponse": {
        "type": "object",
        "required": ["ok"],
        "properties": { "ok": { "type": "boolean", "enum": [true] } },
        "additionalProperties": false
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["ok", "error"],
        "properties": {
          "ok": { "type": "boolean", "enum": [false] },
          "error": { "type": "string" }
        },
        "additionalProperties": true
      },
      "AuditResponse": {
        "type": "object",
        "required": ["ok"],
        "properties": {
          "ok": { "type": "boolean" },
          "mode": { "type": "string" },
          "error": { "type": "string" },
          "notes": { "type": "array", "items": { "type": "string" } },
          "sources": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "rules": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "artifacts": { "type": "object", "additionalProperties": true }
        },
        "additionalProperties": true,
        "description": "Audit output varies by workflow and input. Inspect ok, stages, notes, missing evidence and rule results; it is not a refund decision."
      },
      "TrialRegistration": {
        "type": "object",
        "required": ["name", "email", "company", "role", "company_type", "authorized", "contact_acknowledged"],
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "company": { "type": "string", "maxLength": 160 },
          "role": { "type": "string", "maxLength": 120 },
          "company_type": { "type": "string", "enum": ["3pl", "brand", "consultant", "other"] },
          "authorized": { "type": "boolean", "enum": [true] },
          "contact_acknowledged": { "type": "boolean", "enum": [true] },
          "monthly_volume": { "type": "string", "maxLength": 80 },
          "wms": { "type": "string", "maxLength": 120 },
          "carriers": { "type": "string", "maxLength": 200 }
        },
        "additionalProperties": true
      },
      "TrialRegistrationResponse": {
        "type": "object",
        "required": ["ok", "token", "expires_at"],
        "properties": {
          "ok": { "type": "boolean", "enum": [true] },
          "token": { "type": "string", "description": "Secret bearer token; store securely and do not log." },
          "expires_at": { "type": "string", "format": "date-time" }
        },
        "additionalProperties": false
      },
      "EarlyAccessRequest": {
        "type": "object",
        "required": ["email", "store_url"],
        "properties": {
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "store_url": { "type": "string", "format": "uri", "maxLength": 2048, "description": "Public storefront URL. The server stores its origin." }
        },
        "additionalProperties": true
      },
      "ReviewEnquiry": {
        "type": "object",
        "required": ["name", "email", "company", "role", "company_type", "billing_question", "contact_acknowledged"],
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "company": { "type": "string", "maxLength": 160 },
          "role": { "type": "string", "maxLength": 120 },
          "company_type": { "type": "string", "enum": ["3pl", "brand", "consultant", "other"] },
          "purpose": { "type": "string", "enum": ["review", "assisted"] },
          "billing_question": { "type": "string", "maxLength": 1500 },
          "contact_acknowledged": { "type": "boolean", "enum": [true] },
          "monthly_volume": { "type": "string", "maxLength": 80 },
          "wms": { "type": "string", "maxLength": 120 },
          "carriers": { "type": "string", "maxLength": 200 }
        },
        "additionalProperties": true
      },
      "ProviderEnquiry": {
        "type": "object",
        "required": ["name", "email", "company", "website", "wms", "monthly_volume", "source_page", "contact_acknowledged"],
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "company": { "type": "string", "maxLength": 160 },
          "website": { "type": "string", "format": "uri", "maxLength": 300 },
          "wms": { "type": "string", "maxLength": 120 },
          "monthly_volume": { "type": "string", "enum": ["Under 10,000", "10,000–50,000", "50,001–250,000", "Over 250,000", "Not sure"] },
          "billing_question": { "type": "string", "maxLength": 1500 },
          "source_page": { "type": "string", "enum": ["/", "/3pl"] },
          "contact_acknowledged": { "type": "boolean", "enum": [true] },
          "utm_source": { "type": "string", "maxLength": 200 },
          "utm_medium": { "type": "string", "maxLength": 200 },
          "utm_campaign": { "type": "string", "maxLength": 200 }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "Error": {
        "description": "Request rejected or service unavailable.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
      }
    }
  }
}
