{
  "openapi": "3.1.0",
  "info": {
    "title": "WHATTA Operating Shop API",
    "version": "2026.10",
    "description": "Production shop order and shipment routes. validateOnly is validation only. Carrier submission is fail-closed behind an administrator-approved member-owned or server-selected platform binding and an operational gate; live carrier submission, official tracking-number issuance, cancellation, and official carrier labels are not verified by this document. Use https://sandbox.whatta.co.kr/openapi.json for the isolated mock Sandbox."
  },
  "servers": [{"url": "https://www.whatta.co.kr"}],
  "security": [{"bearerAuth": []}],
  "paths": {
    "/api/shop/orders/": {
      "post": {
        "summary": "Validate or receive a shop order",
        "description": "validateOnly=true performs validation without storing a WHATTA order. ORDER_WRITE is required for a non-validation write. CREATED is not a carrier booking result.",
        "operationId": "validateOrCreateShopOrder",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/OrderRequest"}}}},
        "responses": {
          "200": {"description": "Validation or order result", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "413": {"$ref": "#/components/responses/PayloadTooLarge"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "503": {"$ref": "#/components/responses/NotReady"}
        }
      }
    },
    "/api/shop/shipments/": {
      "get": {
        "summary": "Get a tenant-owned shipment status",
        "parameters": [{"$ref": "#/components/parameters/ShopOrderId"}, {"$ref": "#/components/parameters/OrderNo"}],
        "responses": {
          "200": {"description": "Shipment status", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      },
      "post": {
        "summary": "Validate or request shipment submission",
        "description": "validateOnly=true validates the payload without storing a shipment or calling a carrier. It reports carrierEligibility=APPROVED only when the server-side binding and carrier prerequisites are approved; a missing, pending, disabled, or unapproved binding returns VALIDATION_OK with carrierEligibility=NOT_APPROVED and does not mean carrier approval or booking readiness. Owner mismatch and disabled API accounts remain rejected. A non-validation request requires ORDER_WRITE, an administrator-approved active binding, carrier credentials, and the operational gate. PLATFORM_OWNED bindings require the separate server-side platform-binding gate and are selected only from the approved server binding; request carrier IDs and contract codes are ignored for authorization. The operational gate and platform-binding gate are disabled by default. UNKNOWN must not be automatically resubmitted.",
        "operationId": "validateOrSubmitShipment",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ShipmentRequest"}}}},
        "responses": {
          "200": {"description": "Validation or existing result", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
          "202": {"description": "Result is pending or unknown", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "409": {"$ref": "#/components/responses/Conflict"},
          "429": {"$ref": "#/components/responses/RateLimited"},
          "502": {"$ref": "#/components/responses/CarrierFailure"},
          "503": {"$ref": "#/components/responses/NotReady"}
        }
      }
    },
    "/api/shop/shipments/label/": {
      "get": {
        "summary": "Get an authenticated WHATTA label session result",
        "description": "Requires tenant ownership, an accepted shipment, a valid tracking number, and a WHATTA print session. This is not a verified official carrier label download.",
        "parameters": [{"$ref": "#/components/parameters/ShopOrderId"}],
        "responses": {
          "200": {"description": "SVG label from an authenticated WHATTA output flow", "content": {"image/svg+xml": {"schema": {"type": "string"}}}},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "409": {"$ref": "#/components/responses/Conflict"},
          "410": {"description": "LABEL_SESSION_EXPIRED"},
          "422": {"description": "LABEL_RENDER_UNAVAILABLE"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      },
      "post": {
        "summary": "Mark a WHATTA label as printed",
        "description": "Records the output status for a tenant-owned shipment. It does not call a carrier.",
        "requestBody": {"required": true, "content": {"application/x-www-form-urlencoded": {"schema": {"type": "object", "required": ["shopOrderId", "action"], "properties": {"shopOrderId": {"type": "string"}, "action": {"const": "mark_printed"}}}}}},
        "responses": {
          "200": {"description": "Print status result", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
          "400": {"$ref": "#/components/responses/BadRequest"},
          "401": {"$ref": "#/components/responses/Unauthorized"},
          "403": {"$ref": "#/components/responses/Forbidden"},
          "404": {"$ref": "#/components/responses/NotFound"},
          "429": {"$ref": "#/components/responses/RateLimited"}
        }
      }
    }
  },
  "components": {
    "securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer"}},
    "parameters": {
      "ShopOrderId": {"name": "shopOrderId", "in": "query", "required": false, "schema": {"type": "string"}},
      "OrderNo": {"name": "orderNo", "in": "query", "required": false, "schema": {"type": "string"}}
    },
    "schemas": {
      "Address": {"type": "object", "required": ["name", "phone", "zipcode", "address1"], "properties": {"name": {"type": "string"}, "phone": {"type": "string"}, "zipcode": {"type": "string"}, "address1": {"type": "string"}, "address2": {"type": "string"}}},
      "Product": {"type": "object", "required": ["name", "quantity"], "properties": {"name": {"type": "string"}, "quantity": {"type": "integer", "minimum": 1}}},
      "OrderRequest": {"type": "object", "required": ["provider", "receiver"], "properties": {"provider": {"type": "string", "enum": ["GNUBOARD", "CAFE24", "SMARTSTORE", "GODOMALL", "MAKESHOP", "CUSTOM"]}, "validateOnly": {"type": "boolean"}, "shopOrderId": {"type": "string"}, "orderNo": {"type": "string"}, "receiver": {"$ref": "#/components/schemas/Address"}, "items": {"type": "array", "items": {"$ref": "#/components/schemas/Product"}}, "products": {"type": "array", "items": {"$ref": "#/components/schemas/Product"}}}},
      "ShipmentRequest": {"type": "object", "required": ["carrier", "idempotencyKey", "sender", "receiver", "product"], "properties": {"validateOnly": {"type": "boolean"}, "carrier": {"type": "string", "enum": ["CJ", "POST_OFFICE", "LOTTE"]}, "shopOrderId": {"type": "string"}, "orderNo": {"type": "string"}, "idempotencyKey": {"type": "string"}, "sender": {"$ref": "#/components/schemas/Address"}, "receiver": {"$ref": "#/components/schemas/Address"}, "product": {"$ref": "#/components/schemas/Product"}, "invoiceNo": {"type": "string"}, "trackingNumber": {"type": "string"}}},
      "ApiResponse": {"type": "object", "properties": {"success": {"type": "boolean"}, "status": {"type": "string"}, "error_code": {"type": ["string", "null"]}, "message": {"type": "string"}, "data": {"type": ["object", "null"], "description": "For shipment validation, data may include validateOnly, carrierSubmission=NOT_EXECUTED, contractCheck (BINDING_APPROVED, BINDING_NOT_APPROVED, or BINDING_NOT_FOUND), carrierEligibility (APPROVED or NOT_APPROVED), carrierPreflight=NOT_EXECUTED, and quota."}}}
    },
    "responses": {
      "BadRequest": {"description": "Invalid request", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "Unauthorized": {"description": "Authentication required or token invalid", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "Forbidden": {"description": "Scope, member ownership, contract binding, or approval denied", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "NotFound": {"description": "Member-owned resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "Conflict": {"description": "Payload conflict or label not ready", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "RateLimited": {"description": "Route or API quota limit", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "CarrierFailure": {"description": "Carrier result not confirmed", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}},
      "NotReady": {"description": "Operational gate or required schema/configuration is not ready", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ApiResponse"}}}}
    }
  }
}
