{
 "openapi": "3.0.3",
 "info": {
  "title": "NODA Engine",
  "version": "0.1.0",
  "description": "한글·PDF·스캔 공문서를 정확히 읽어 근거가 붙은 조각으로 LLM 에게 넘기는 RAG 바탕 엔진. 못 읽어도 200 이고 JSON 의 access.reason 이 까닭이다. 뜻은 docs/api.md · docs/schema.md."
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   },
   "apiKey": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   }
  }
 },
 "paths": {
  "/health": {
   "get": {
    "summary": "살아 있는가 · 포맷 · 엔드포인트 · 키 여부 · 상한 · 요청 제한",
    "responses": {
     "200": {
      "description": "ok",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/openapi.json": {
   "get": {
    "summary": "이 명세",
    "responses": {
     "200": {
      "description": "OpenAPI 3",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/parse": {
   "post": {
    "summary": "파일 하나 → 계약의 문서 JSON",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "X-Filename",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "퍼센트 인코딩한 파일 이름 (또는 ?filename=)"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      },
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "filename": {
          "type": "string"
         },
         "data_base64": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Document (docs/schema.md)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "요청이 틀렸다",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "413": {
      "description": "너무 크다",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "429": {
      "description": "분당 상한 (Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "503": {
      "description": "동시 파싱 상한 (Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/chunk": {
   "post": {
    "summary": "파일 하나 → 문서 + 조각",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "X-Filename",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "퍼센트 인코딩한 파일 이름 (또는 ?filename=)"
     },
     {
      "name": "budget",
      "in": "query",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      },
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "filename": {
          "type": "string"
         },
         "data_base64": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "{document, chunks[]}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "503": {
      "description": "동시 파싱 상한 (Retry-After)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/documents": {
   "get": {
    "summary": "테넌트에 무엇이 들어와 있고 무엇을 못 읽었나 · 서식 칸",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "tenant",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "principal",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "{documents[], readable, unreadable, needs_review}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   },
   "delete": {
    "summary": "테넌트에서 문서를 뺀다 (키 있는 서버만)",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "tenant",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "filename",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "deleted",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "403": {
      "description": "키 없는 서버",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "404": {
      "description": "없다",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/documents/upload": {
   "post": {
    "summary": "테넌트에 문서를 넣고 바로 읽는다 (키 있는 서버만)",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "tenant",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "X-Filename",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "퍼센트 인코딩한 파일 이름 (또는 ?filename=)"
     },
     {
      "name": "replace",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      },
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "filename": {
          "type": "string"
         },
         "data_base64": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "stored + readable/reason/chunks",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "409": {
      "description": "같은 이름",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "403": {
      "description": "키 없는 서버",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/context": {
   "post": {
    "summary": "LLM 에게 넘길 근거 묶음 — [n] 번호 · 출처 · 쪽 · 글자 예산",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "tenant",
         "query"
        ],
        "properties": {
         "tenant": {
          "type": "string"
         },
         "query": {
          "type": "string"
         },
         "budget": {
          "type": "integer",
          "default": 4000
         },
         "limit": {
          "type": "integer"
         },
         "principal": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "{context, sources[], truncated, used, budget}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/verify": {
   "post": {
    "summary": "소비자의 답을 근거에 대고 본다 — 근거에 없는 숫자 · 조각에 없는 인용 · 없는 번호를 자리로",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "tenant",
         "answer"
        ],
        "properties": {
         "tenant": {
          "type": "string"
         },
         "answer": {
          "type": "string"
         },
         "sources": {
          "type": "array",
          "items": {
           "type": "object",
           "required": [
            "doc_id",
            "chunk_id"
           ]
          }
         },
         "citations": {
          "type": "array",
          "items": {
           "type": "object"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "{grounded, sentences[{text, refs, numbers, unsupported}], citations[{found}], unknown_refs, unsupported_numbers}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/search": {
   "post": {
    "summary": "조각 + 인용 근거",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "tenant",
         "query"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "{hits[]}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/answer": {
   "post": {
    "summary": "발췌 답(지어내지 않음) · llm:true 면 인용을 검증하는 게이트웨이",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "tenant",
         "question"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "{text, citations[], reason, made_by}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/view": {
   "get": {
    "summary": "근거를 원문 위에 칠한 화면 (HTML)",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "parameters": [
     {
      "name": "tenant",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "question",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "HTML"
     }
    }
   }
  },
  "/mcp": {
   "post": {
    "summary": "MCP (Streamable HTTP · JSON-RPC 2.0) — 에이전트 플랫폼이 읽기·찾기·근거·검증을 도구로 부른다 (docs/mcp.md)",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "jsonrpc",
         "method"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "JSON-RPC 답",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "202": {
      "description": "알림만 왔다 — 몸 없음"
     },
     "403": {
      "description": "다른 출처",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  },
  "/usage": {
   "get": {
    "summary": "소비자별 사용량 (키 있는 서버만)",
    "security": [
     {
      "bearer": []
     },
     {
      "apiKey": []
     }
    ],
    "responses": {
     "200": {
      "description": "{consumers: {이름: {길: {calls, errors, bytes_in, ms}}}}",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   }
  }
 }
}