{
  "openapi": "3.1.0",
  "info": {
    "title": "usedocs API",
    "version": "1.0.0",
    "summary": "Cited answers from your documentation for widgets, tickets, and agents.",
    "description": "Public Answer API and widget endpoints for usedocs. Authenticate widget calls with botId + botKey (from Dashboard → Settings → Embed). Dashboard management APIs use the signed-in session cookie and are documented on https://usedocs.app/developers. Rate-limit conventions: RFC 9237 RateLimit / RateLimit-Policy headers, plus Retry-After on HTTP 429. Markdown pages: send Accept: text/markdown to HTML URLs.",
    "contact": {
      "name": "usedocs",
      "email": "hello@usedocs.app",
      "url": "https://usedocs.app/contact"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "servers": [
    {
      "url": "https://usedocs.app",
      "description": "Production"
    },
    {
      "url": "https://staging.usedocs.app",
      "description": "Staging"
    }
  ],
  "tags": [
    {
      "name": "Answer",
      "description": "Cited RAG answers for tickets, agents, and the chat widget."
    },
    {
      "name": "Widget",
      "description": "Embed configuration, search, leads, and feedback."
    },
    {
      "name": "Discovery",
      "description": "Machine-readable specs, MCP, and app config."
    }
  ],
  "paths": {
    "/chat": {
      "post": {
        "operationId": "chatAsk",
        "tags": [
          "Answer"
        ],
        "summary": "Ask a cited question",
        "description": "Returns a grounded answer from the bot's documentation. Low-confidence questions set escalate=true instead of inventing. Rate limit: 30 requests / 60 seconds per visitor (policy widget-chat).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cited answer",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chat/stream": {
      "post": {
        "operationId": "chatStream",
        "tags": [
          "Answer"
        ],
        "summary": "Ask a cited question (SSE)",
        "description": "Same body as chatAsk. Server-Sent Events: start → token* → result (or error). Same widget-chat rate limit as POST /chat.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "text/event-stream of start, token, result, or error events",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/chat/suggest": {
      "post": {
        "operationId": "chatSuggest",
        "tags": [
          "Answer"
        ],
        "summary": "Suggested follow-up questions",
        "description": "Returns follow-up prompts for an existing conversation.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "botId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "botKey": {
                    "type": "string"
                  },
                  "conversationId": {
                    "type": "string"
                  }
                },
                "required": [
                  "botId",
                  "conversationId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggestions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suggestions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "suggestions"
                  ]
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/config": {
      "get": {
        "operationId": "getWidgetConfig",
        "tags": [
          "Widget"
        ],
        "summary": "Public widget configuration",
        "description": "Colors, welcome message, Turnstile flags, help-center URL, and suggested questions.",
        "parameters": [
          {
            "name": "botId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Bot UUID from the dashboard."
          }
        ],
        "responses": {
          "200": {
            "description": "Widget config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetConfig"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/docs/search": {
      "get": {
        "operationId": "searchHelpArticles",
        "tags": [
          "Widget"
        ],
        "summary": "Search published help articles",
        "description": "Public search over the bot's published help-center articles.",
        "parameters": [
          {
            "name": "botId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "botKey",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "locale",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching articles",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArticleSearchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/announce": {
      "get": {
        "operationId": "getAnnounceFeed",
        "tags": [
          "Widget"
        ],
        "summary": "Latest changelog entries for the in-app announce banner",
        "parameters": [
          {
            "name": "botId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Announce payload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/lead": {
      "post": {
        "operationId": "captureLead",
        "tags": [
          "Widget"
        ],
        "summary": "Capture a visitor lead from the widget",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "botId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "botKey": {
                    "type": "string"
                  },
                  "conversationId": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "name": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "botId",
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/feedback": {
      "post": {
        "operationId": "submitFeedback",
        "tags": [
          "Widget"
        ],
        "summary": "Rate a conversation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "botId": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "botKey": {
                    "type": "string"
                  },
                  "conversationId": {
                    "type": "string"
                  },
                  "rating": {
                    "type": "string",
                    "enum": [
                      "up",
                      "down",
                      "yes",
                      "no"
                    ]
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "botId",
                  "conversationId",
                  "rating"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feedback stored",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/app-config": {
      "get": {
        "operationId": "getAppConfig",
        "tags": [
          "Discovery"
        ],
        "summary": "Public app config for the dashboard shell",
        "responses": {
          "200": {
            "description": "Auth base URL and public flags",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiSpec",
        "tags": [
          "Discovery"
        ],
        "summary": "OpenAPI 3.1 specification",
        "description": "This document. Use it for LLM function-calling and client generation.",
        "responses": {
          "200": {
            "description": "OpenAPI document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "get": {
        "operationId": "getMcpServerCard",
        "tags": [
          "Discovery"
        ],
        "summary": "usedocs MCP server card",
        "description": "Discovery document for the usedocs Model Context Protocol server.",
        "responses": {
          "200": {
            "description": "MCP server card",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/McpServerCard"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "mcpJsonRpc",
        "tags": [
          "Discovery"
        ],
        "summary": "MCP Streamable HTTP JSON-RPC",
        "description": "Model Context Protocol endpoint (JSON-RPC 2.0). Tools: ask_docs, search_docs, get_bot_config. Pass botId and botKey in tool arguments. See https://usedocs.app/developers#mcp.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "RateLimitPolicy": {
        "description": "RFC 9237 quota policy. Example: \"widget-chat\";q=30;w=60",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit": {
        "description": "RFC 9237 remaining quota. Example: \"widget-chat\";r=29;t=60",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying a 429.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Structured JSON error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limited. Honor Retry-After and RateLimit headers.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait before retrying."
          },
          "RateLimit-Policy": {
            "schema": {
              "type": "string"
            }
          },
          "RateLimit": {
            "schema": {
              "type": "string"
            }
          },
          "RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiError"
            }
          }
        }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": [
          "error",
          "code",
          "message",
          "hint"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error (legacy field; same as message)."
          },
          "code": {
            "type": "string",
            "description": "Stable machine code: invalid_request, forbidden, not_found, rate_limited, …"
          },
          "message": {
            "type": "string"
          },
          "hint": {
            "type": "string",
            "description": "How to resolve the error."
          }
        }
      },
      "ChatRequest": {
        "type": "object",
        "required": [
          "botId",
          "question"
        ],
        "properties": {
          "botId": {
            "type": "string",
            "format": "uuid",
            "description": "Bot UUID from the dashboard."
          },
          "botKey": {
            "type": "string",
            "description": "Public bot key from Settings → Embed."
          },
          "question": {
            "type": "string",
            "minLength": 1,
            "description": "Visitor or agent question."
          },
          "conversationId": {
            "type": "string",
            "description": "Continue an existing thread."
          },
          "visitor": {
            "type": "string",
            "description": "Stable visitor or ticket id for rate limiting."
          },
          "pageUrl": {
            "type": "string",
            "format": "uri",
            "description": "Page the visitor was on."
          }
        }
      },
      "Citation": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          }
        }
      },
      "ChatResponse": {
        "type": "object",
        "properties": {
          "conversationId": {
            "type": "string"
          },
          "answer": {
            "type": "string"
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Citation"
            }
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "escalate": {
            "type": "boolean"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "WidgetConfig": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "welcomeMessage": {
            "type": "string"
          },
          "primaryColor": {
            "type": "string"
          },
          "requiresBotKey": {
            "type": "boolean"
          },
          "available": {
            "type": "boolean"
          },
          "helpBase": {
            "type": "string"
          }
        }
      },
      "ArticleSearchResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "articles": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "McpServerCard": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "transport": {
            "type": "string"
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {},
          "method": {
            "type": "string"
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {},
          "result": {},
          "error": {
            "type": "object"
          }
        }
      }
    }
  }
}