{
  "openapi": "3.0.3",
  "info": {
    "title": "MDM Gate API",
    "version": "1.0.0",
    "description": "출입 게이트 스캐너 연동 API. 휴대폰의 출입 QR(MDMPASS:{device_id}:{code}, 5분마다 갱신)을 스캔해 입실/퇴실을 보고하면 서버가 기기에 카메라 차단/해제 명령을 보냅니다. 문서: https://mdm.pino-like.com/api/gate/docs"
  },
  "servers": [{ "url": "https://mdm.pino-like.com" }],
  "security": [{ "bearerAuth": [] }, { "apiKeyHeader": [] }],
  "tags": [{ "name": "gate", "description": "출입 게이트" }],
  "paths": {
    "/api/gate/v1/entry": {
      "post": {
        "tags": ["gate"],
        "operationId": "gateEntry",
        "summary": "입실 — 카메라 차단 명령 전송",
        "requestBody": { "required": true, "content": { "application/json": {
          "schema": { "$ref": "#/components/schemas/GateEvent" },
          "example": { "pass": "MDMPASS:dev_1a2b3c4d5e6f:305109", "gate_id": "pangyo-4f-g2",
                       "gate_name": "판교 R&D 4층 게이트 2", "event_id": "g2-20260929-000123", "ts": 1790000000 } } } },
        "responses": {
          "200": { "description": "수락됨 (event_id 재전송이면 duplicate: true)", "content": { "application/json": {
            "schema": { "$ref": "#/components/schemas/GateResult" },
            "example": { "result": "accepted", "device_id": "dev_1a2b3c4d5e6f", "device_name": "홍길동 Galaxy S24",
                         "command_id": 812, "camera": "block_pending", "gate_inside": true, "was_inside": false,
                         "gate_id": "pangyo-4f-g2", "gate_name": "판교 R&D 4층 게이트 2",
                         "event_id": "g2-20260929-000123", "server_time": 1790000000.412 } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/UnknownDevice" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "413": { "$ref": "#/components/responses/TooLarge" },
          "422": { "$ref": "#/components/responses/Validation" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/gate/v1/exit": {
      "post": {
        "tags": ["gate"],
        "operationId": "gateExit",
        "summary": "퇴실 — 카메라 차단 해제 명령 전송",
        "requestBody": { "required": true, "content": { "application/json": {
          "schema": { "$ref": "#/components/schemas/GateEvent" },
          "example": { "pass": "MDMPASS:dev_1a2b3c4d5e6f:512877", "gate_id": "pangyo-4f-g2",
                       "gate_name": "판교 R&D 4층 게이트 2", "event_id": "g2-20260929-000187" } } } },
        "responses": {
          "200": { "description": "수락됨. 게이트 안이 아니었어도 200 (was_inside: false)", "content": { "application/json": {
            "schema": { "$ref": "#/components/schemas/GateResult" },
            "example": { "result": "accepted", "device_id": "dev_1a2b3c4d5e6f", "device_name": "홍길동 Galaxy S24",
                         "command_id": 815, "camera": "release_pending", "gate_inside": false, "was_inside": true,
                         "gate_id": "pangyo-4f-g2", "gate_name": "판교 R&D 4층 게이트 2",
                         "event_id": "g2-20260929-000187", "server_time": 1790003600.087 } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/UnknownDevice" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "413": { "$ref": "#/components/responses/TooLarge" },
          "422": { "$ref": "#/components/responses/Validation" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/gate/v1/verify": {
      "post": {
        "tags": ["gate"],
        "operationId": "gateVerify",
        "summary": "출입 코드 확인만 (부수 효과 없음)",
        "requestBody": { "required": true, "content": { "application/json": {
          "schema": { "$ref": "#/components/schemas/PassOnly" },
          "example": { "pass": "MDMPASS:dev_1a2b3c4d5e6f:305109" } } } },
        "responses": {
          "200": { "description": "검증 결과 (코드가 틀려도 200, valid: false)", "content": { "application/json": {
            "schema": { "$ref": "#/components/schemas/VerifyResult" },
            "example": { "valid": true, "device_id": "dev_1a2b3c4d5e6f", "device_name": "홍길동 Galaxy S24" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/UnknownDevice" },
          "422": { "$ref": "#/components/responses/Validation" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/gate/v1/devices/{device_id}": {
      "get": {
        "tags": ["gate"],
        "operationId": "gateDevice",
        "summary": "기기 상태 조회",
        "parameters": [{ "name": "device_id", "in": "path", "required": true, "schema": { "type": "string" }, "example": "dev_1a2b3c4d5e6f" }],
        "responses": {
          "200": { "description": "기기 상태", "content": { "application/json": {
            "schema": { "$ref": "#/components/schemas/DeviceStatus" },
            "example": { "device_id": "dev_1a2b3c4d5e6f", "device_name": "홍길동 Galaxy S24", "online": true,
                         "last_checkin": 1790000003.2, "camera_disabled": true, "gate_inside": true,
                         "gate_id": "pangyo-4f-g2", "gate_name": "판교 R&D 4층 게이트 2", "zone": "out",
                         "compliant": true, "agent_version": "1.4.0" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/UnknownDevice" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/gate/v1/health": {
      "get": {
        "tags": ["gate"],
        "operationId": "gateHealth",
        "summary": "연결·토큰 확인",
        "responses": {
          "200": { "description": "정상", "content": { "application/json": {
            "schema": { "type": "object", "properties": {
              "ok": { "type": "boolean" }, "server_time": { "type": "number" },
              "api_version": { "type": "string" }, "token": { "type": "string", "description": "토큰 이름" } } },
            "example": { "ok": true, "server_time": 1790000000.412, "api_version": "v1", "token": "판교 4층 게이트" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Authorization: Bearer mdmg_… (콘솔 → 연동 → API 토큰)" },
      "apiKeyHeader": { "type": "apiKey", "in": "header", "name": "X-API-Key" }
    },
    "schemas": {
      "GateEvent": {
        "type": "object",
        "required": ["gate_id"],
        "description": "pass 또는 device_id + code 중 하나는 필수",
        "properties": {
          "pass": { "type": "string", "description": "스캔한 QR 원문 MDMPASS:{device_id}:{code}", "example": "MDMPASS:dev_1a2b3c4d5e6f:305109" },
          "device_id": { "type": "string", "description": "pass 대신 사용" },
          "code": { "type": "string", "description": "6자리 코드 (pass 대신 사용; 공백·•·- 허용)" },
          "gate_id": { "type": "string", "minLength": 1, "maxLength": 64 },
          "gate_name": { "type": "string", "maxLength": 100, "description": "표시 이름 (없으면 gate_id)" },
          "event_id": { "type": "string", "maxLength": 64, "description": "멱등 키 (토큰별로 고유)" },
          "ts": { "type": "number", "description": "게이트 시각 (unix 초, 기록용)" }
        }
      },
      "PassOnly": {
        "type": "object",
        "description": "pass 또는 device_id + code",
        "properties": {
          "pass": { "type": "string" }, "device_id": { "type": "string" }, "code": { "type": "string" }
        }
      },
      "GateResult": {
        "type": "object",
        "properties": {
          "result": { "type": "string", "enum": ["accepted"] },
          "device_id": { "type": "string" },
          "device_name": { "type": "string", "nullable": true },
          "command_id": { "type": "integer" },
          "camera": { "type": "string", "enum": ["block_pending", "already_blocked", "release_pending", "stays_blocked"] },
          "gate_inside": { "type": "boolean" },
          "was_inside": { "type": "boolean" },
          "gate_id": { "type": "string" },
          "gate_name": { "type": "string" },
          "event_id": { "type": "string", "nullable": true },
          "server_time": { "type": "number" },
          "duplicate": { "type": "boolean", "description": "같은 event_id 재전송 — 최초 응답을 그대로 반환, 명령은 다시 보내지 않음" }
        }
      },
      "VerifyResult": {
        "type": "object",
        "properties": { "valid": { "type": "boolean" }, "device_id": { "type": "string" }, "device_name": { "type": "string", "nullable": true } }
      },
      "DeviceStatus": {
        "type": "object",
        "properties": {
          "device_id": { "type": "string" }, "device_name": { "type": "string", "nullable": true },
          "online": { "type": "boolean", "description": "최근 30초 안에 체크인" },
          "last_checkin": { "type": "number", "nullable": true },
          "camera_disabled": { "type": "boolean" }, "gate_inside": { "type": "boolean" },
          "gate_id": { "type": "string", "nullable": true }, "gate_name": { "type": "string", "nullable": true },
          "zone": { "type": "string", "nullable": true, "description": "Wi-Fi 보안구역 상태 off|in|out|unknown" },
          "compliant": { "type": "boolean" }, "agent_version": { "type": "string", "nullable": true }
        }
      },
      "Error": { "type": "object", "properties": { "detail": { "type": "string" } } },
      "ValidationError": {
        "type": "object",
        "properties": { "detail": { "type": "array", "items": { "type": "object", "properties": {
          "type": { "type": "string" }, "loc": { "type": "array", "items": {} }, "msg": { "type": "string" }, "input": {} } } } }
      }
    },
    "responses": {
      "Unauthorized": { "description": "토큰 없음/무효/폐기/만료", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "invalid api token" } } } },
      "Forbidden": { "description": "코드 불일치·만료, scope 없음, 허용되지 않은 gate_id", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "invalid or expired pass" } } } },
      "UnknownDevice": { "description": "등록되지 않은 기기", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "unknown device" } } } },
      "Conflict": { "description": "event_id가 다른 기기/동작에 이미 사용됨", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "event_id already used for entry of device dev_1a2b3c4d5e6f" } } } },
      "TooLarge": { "description": "본문 64 KB 초과", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Validation": { "description": "필드 누락·형식 오류", "content": { "application/json": {
        "schema": { "$ref": "#/components/schemas/ValidationError" },
        "example": { "detail": [{ "type": "missing", "loc": ["body", "gate_id"], "msg": "Field required", "input": {} }] } } } },
      "RateLimited": { "description": "토큰당 초당 20회(버스트 40) 초과", "headers": { "Retry-After": { "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "detail": "rate limit exceeded" } } } }
    }
  }
}
