{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "리옵트 애즈 Agent API",
    "summary": "리옵트 애즈(reopt ads)의 공개 에이전트 엔드포인트 — 마크다운 협상, MCP 서버, 매체 인벤토리 검색.",
    "description": "리옵트 애즈는 광고주가 광고를 요청하고 파트너가 자기 서비스에 들어올 광고를 직접 승인하는 승인형 광고 마켓플레이스입니다.\n\n이 문서는 인증 없이 호출할 수 있는 **읽기 전용** 공개 계약입니다. 캠페인 생성·예산 변경·소재 등록 같은 쓰기 작업은 이 도메인에 공개 API가 없으며, 리옵트 플랫폼 계정으로 로그인한 대시보드에서 수행합니다.\n\n권한 모델과 스코프는 `components.securitySchemes.brandappOAuth2`를 보세요. 사람이 읽는 설명은 https://ads.reopt.ai/developers에 있습니다.",
    "version": "2026-09-06",
    "contact": {
      "name": "주식회사 리옵트 고객지원",
      "email": "contact@reopt.ai",
      "url": "https://ads.reopt.ai/contact"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://reopt.ai/ko/terms/service"
    }
  },
  "externalDocs": {
    "description": "에이전트용 사이트 안내 (llms.txt)",
    "url": "https://ads.reopt.ai/llms.txt"
  },
  "servers": [
    {
      "url": "https://ads.reopt.ai",
      "description": "Production"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "discovery",
      "description": "사이트 구조와 에이전트 안내 파일"
    },
    {
      "name": "markdown",
      "description": "페이지 본문의 마크다운 표현"
    },
    {
      "name": "mcp",
      "description": "Model Context Protocol 서버"
    },
    {
      "name": "search",
      "description": "가이드 문서 검색"
    }
  ],
  "paths": {
    "/llms.txt": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getLlmsTxt",
        "summary": "에이전트용 사이트 안내",
        "description": "언제 이 서비스를 써야 하는지, 어떤 문서와 엔드포인트가 있는지 요약한 llms.txt입니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "llms.txt 본문",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/llms-full.txt": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getLlmsFullTxt",
        "summary": "공개 문서 전문",
        "description": "공개 페이지 전체를 마크다운으로 이어 붙인 단일 파일입니다. 페이지마다 협상 요청을 반복하지 않고 한 번에 컨텍스트로 넣을 때 사용하세요.",
        "security": [],
        "responses": {
          "200": {
            "description": "문서 전문",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getSitemap",
        "summary": "전체 공개 URL 목록",
        "security": [],
        "responses": {
          "200": {
            "description": "sitemap",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/inventory/rss.xml": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getInventoryFeed",
        "summary": "매체 인벤토리 RSS 2.0 피드",
        "description": "매체마다 형식·구매 방식·가격·심사 조건과 공식 출처를 담습니다. 항목은 공식 출처 검증일 순입니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "RSS 2.0",
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tools/rss.xml": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getToolsFeed",
        "summary": "마케팅 도구 RSS 2.0 피드",
        "description": "도구마다 용도·가격·언어·주요 기능을 담습니다. 항목은 정보 확인일 순입니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "RSS 2.0",
            "content": {
              "application/rss+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getOpenApiDocument",
        "summary": "이 문서",
        "description": "동일한 문서를 https://ads.reopt.ai/api/openapi.json에서도 받을 수 있습니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 문서",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/agent/markdown": {
      "get": {
        "tags": [
          "markdown"
        ],
        "operationId": "getPageMarkdownHome",
        "summary": "홈 본문을 마크다운으로 조회",
        "description": "경로 세그먼트 없이 호출하는 홈 문서 형태입니다. `/`에 `Accept: text/markdown`을 보낸 것과 같은 본문을 반환합니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "페이지의 마크다운 본문. `Link: rel=canonical`로 대응하는 HTML URL을 함께 알려준다.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "head": {
        "tags": [
          "markdown"
        ],
        "operationId": "headPageMarkdownHome",
        "summary": "홈 마크다운 표현의 존재 확인",
        "security": [],
        "responses": {
          "200": {
            "description": "문서 있음"
          }
        }
      }
    },
    "/api/agent/markdown/{path}": {
      "get": {
        "tags": [
          "markdown"
        ],
        "operationId": "getPageMarkdown",
        "summary": "페이지 본문을 마크다운으로 조회",
        "description": "모든 공개 페이지 URL에 `Accept: text/markdown`을 보내면 같은 주소가 이 본문을 반환합니다(응답에 `Vary: Accept`가 붙습니다). 협상 헤더를 쓸 수 없는 클라이언트는 이 경로를 직접 호출하세요.",
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "마크다운으로 받을 페이지 경로. 예: `inventory/youtube-shorts`, `guide/approval`.",
            "schema": {
              "type": "string"
            },
            "example": "inventory/youtube-shorts"
          }
        ],
        "responses": {
          "200": {
            "description": "페이지의 마크다운 본문. `Link: rel=canonical`로 대응하는 HTML URL을 함께 알려준다.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "해당 경로에 문서가 없음. 본문은 사이트맵·llms.txt로 돌아가는 링크가 담긴 마크다운이다.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "head": {
        "tags": [
          "markdown"
        ],
        "operationId": "headPageMarkdown",
        "summary": "마크다운 표현의 존재 확인",
        "security": [],
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "마크다운으로 받을 페이지 경로. 예: `inventory/youtube-shorts`, `guide/approval`.",
            "schema": {
              "type": "string"
            },
            "example": "inventory/youtube-shorts"
          }
        ],
        "responses": {
          "200": {
            "description": "문서 있음"
          },
          "404": {
            "description": "문서 없음"
          }
        }
      }
    },
    "/api/mcp": {
      "get": {
        "tags": [
          "mcp"
        ],
        "operationId": "getMcpManifest",
        "summary": "MCP 서버 매니페스트",
        "description": "발견용 매니페스트를 반환합니다. `Accept: text/event-stream`으로 요청하면 405 — 이 서버는 SSE 스트림을 열지 않습니다.",
        "security": [],
        "responses": {
          "200": {
            "description": "매니페스트",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpManifest"
                }
              }
            }
          },
          "405": {
            "description": "SSE 스트림 미지원"
          }
        }
      },
      "post": {
        "tags": [
          "mcp"
        ],
        "operationId": "callMcp",
        "summary": "MCP JSON-RPC 호출",
        "description": "Streamable HTTP transport, protocol 2025-06-18. 인증이 없고 상태를 두지 않으므로 세션 ID를 발급하지 않습니다. 읽기 전용 도구만 제공합니다: `search_inventory`, `get_inventory_item`, `list_guides`, `get_guide`. 요청 하나에 JSON 응답 하나로 끝나고, 알림(id 없는 메시지)만 보낸 경우 202를 반환합니다.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 응답",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "알림만 담긴 요청 — 돌려줄 결과 없음"
          },
          "400": {
            "description": "JSON-RPC 파싱 실패 또는 빈 배치"
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "tags": [
          "search"
        ],
        "operationId": "searchGuides",
        "summary": "가이드 문서 검색",
        "description": "이용 가이드(MDX) 전문 검색입니다. `query`가 비어 있으면 빈 배열을 반환합니다.",
        "security": [],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "description": "검색어",
            "schema": {
              "type": "string"
            },
            "example": "승인 절차"
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "쉼표로 구분한 태그 필터",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "full",
                "vector"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "검색 결과",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SearchResult"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "brandappOAuth2": {
        "type": "oauth2",
        "description": "리옵트 플랫폼(Brandapp) OAuth 2.0. 리옵트 애즈 대시보드 로그인이 쓰는 인가 방식입니다. 리옵트 애즈가 요청하는 스코프는 `openid`, `profile`, `email` 세 개뿐이며, 광고 데이터 쓰기 스코프는 요청하지 않습니다. 에이전트는 필요한 최소 스코프만 요청하세요 — 읽기는 `*:read`, 변경은 해당 리소스의 `*:write`가 필요합니다. 이 OpenAPI 문서가 기술하는 공개 엔드포인트는 모두 인증이 필요 없습니다(`security: []`).",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://id.reopt.ai/oauth/authorize",
            "tokenUrl": "https://brandapp.reopt.ai/v1/brandapp/{id}/auth/token",
            "refreshUrl": "https://brandapp.reopt.ai/v1/brandapp/{id}/auth/refresh",
            "scopes": {
              "openid": "OpenID Connect 기본 인증",
              "profile": "사용자 프로필 정보 읽기",
              "email": "사용자 이메일 정보 읽기",
              "brandapp:read": "Brandapp 리소스 읽기",
              "brandapp:write": "Brandapp 리소스 쓰기",
              "eav:read": "EAV 엔티티·레코드 읽기",
              "eav:write": "EAV 엔티티·레코드 쓰기",
              "workflow:execute": "워크플로우 실행"
            }
          }
        }
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "description": "생략하면 알림(notification)으로 처리됩니다.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "examples": [
              "initialize",
              "tools/list",
              "tools/call",
              "ping"
            ]
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc",
          "id"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "McpManifest": {
        "type": "object",
        "required": [
          "name",
          "description",
          "protocolVersion",
          "transport",
          "url",
          "instructions",
          "documentation"
        ],
        "properties": {
          "name": {
            "type": "string",
            "const": "reopt-ads"
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "protocolVersion": {
            "type": "string"
          },
          "transport": {
            "type": "string",
            "const": "streamable-http"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "authentication": {
            "type": "string",
            "const": "none"
          },
          "access": {
            "type": "string",
            "const": "read-only"
          },
          "instructions": {
            "type": "string"
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "documentation": {
            "type": "object",
            "required": [
              "developers",
              "llmsTxt",
              "openapi"
            ],
            "properties": {
              "developers": {
                "type": "string",
                "format": "uri"
              },
              "llmsTxt": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "page",
              "heading",
              "text"
            ]
          },
          "content": {
            "type": "string"
          }
        }
      }
    }
  }
}