{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Botpaid Public API",
    "version": "2026-04-21",
    "summary": "Public service description for Botpaid's hosted MCP transport and browser-authenticated web API.",
    "description": "Botpaid is a hosted MCP server and web API for pay-as-you-go AI image, video, and audio generation. The production MCP endpoint is https://app.botpaid.com/mcp. Browser-authenticated REST endpoints live on https://app.botpaid.com/api/*."
  },
  "servers": [
    {
      "url": "https://app.botpaid.com",
      "description": "Production API and hosted MCP service"
    }
  ],
  "externalDocs": {
    "description": "Botpaid developer documentation",
    "url": "https://botpaid.com/docs"
  },
  "tags": [
    {
      "name": "MCP",
      "description": "Hosted MCP transport endpoints."
    },
    {
      "name": "Status",
      "description": "Health and request status endpoints."
    },
    {
      "name": "Account",
      "description": "Authenticated account, balance, and billing routes."
    }
  ],
  "security": [
    {
      "cookieSession": []
    },
    {
      "oauth2AuthCode": []
    }
  ],
  "paths": {
    "/mcp": {
      "get": {
        "tags": ["MCP"],
        "summary": "Open an optional server-sent event stream for the hosted MCP transport.",
        "description": "The Botpaid Streamable HTTP MCP endpoint supports GET for optional server-to-client event streams. Clients should send `Accept: text/event-stream`.",
        "operationId": "openMcpStream",
        "responses": {
          "200": {
            "description": "Server-sent events stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      },
      "post": {
        "tags": ["MCP"],
        "summary": "Send a JSON-RPC request or batch to the hosted Botpaid MCP server.",
        "description": "The production MCP transport lives at `/mcp` and uses Streamable HTTP. Clients should send JSON-RPC requests with `Accept: application/json, text/event-stream`.",
        "operationId": "postMcpMessage",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/McpJsonRpcRequest"
                  },
                  {
                    "$ref": "#/components/schemas/McpJsonRpcBatch"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response or a streamed response depending on the request.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "JSON-RPC response envelope"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "202": {
            "description": "Notification or response batch accepted with no body."
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitedResponse"
          }
        }
      }
    },
    "/status": {
      "get": {
        "tags": ["Status"],
        "summary": "Return a machine-readable service status summary.",
        "operationId": "getServiceStatus",
        "security": [],
        "responses": {
          "200": {
            "description": "Current status and discovery links.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublicStatus"
                }
              }
            }
          }
        }
      }
    },
    "/api/status/{requestId}": {
      "get": {
        "tags": ["Status"],
        "summary": "Look up the status of a previously created generation request.",
        "description": "This route is intentionally public and returns a minimal status payload for a generation request.",
        "operationId": "getGenerationStatus",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/RequestId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current generation status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenerationStatus"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "500": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "tags": ["Account"],
        "summary": "List models currently available to the authenticated user.",
        "operationId": "listModels",
        "responses": {
          "200": {
            "description": "Available models and metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Model"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/api/balance": {
      "get": {
        "tags": ["Account"],
        "summary": "Return the authenticated user's current balance and refund eligibility.",
        "operationId": "getBalance",
        "responses": {
          "200": {
            "description": "Current balance and refundability summary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/api/transactions": {
      "get": {
        "tags": ["Account"],
        "summary": "Return authenticated transaction history.",
        "operationId": "listTransactions",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "description": "Optional comma-separated transaction types.",
            "schema": {
              "type": "string",
              "example": "purchase,usage"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of transactions to return.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Offset for pagination.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "provider",
            "in": "query",
            "description": "Optional provider filter.",
            "schema": {
              "type": "string",
              "example": "vertex"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction history.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/api/checkout": {
      "post": {
        "tags": ["Account"],
        "summary": "Create a Stripe checkout session to add credits.",
        "description": "Checkout uses the authenticated browser session and currently accepts a small set of predefined credit packages.",
        "operationId": "createCheckoutSession",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount"],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "enum": [1, 5, 10, 20, 50],
                    "description": "Credit package in USD."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout session created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitedResponse"
          }
        }
      }
    },
    "/api/withdraw": {
      "post": {
        "tags": ["Account"],
        "summary": "Preview or confirm a refund of unused balance.",
        "operationId": "requestWithdrawal",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "confirm": {
                    "type": "boolean",
                    "default": false,
                    "description": "Set true to execute the refund."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preview or completed refund result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WithdrawResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "429": {
            "$ref": "#/components/responses/RateLimitedResponse"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "cookieSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "botpaid_session",
        "description": "Browser session established after GitHub or Google sign-in on app.botpaid.com."
      },
      "oauth2AuthCode": {
        "type": "oauth2",
        "description": "Hosted OAuth flow used by remote MCP clients. Botpaid's scopes are currently coarse-grained and client-dependent.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.botpaid.com/github/authorize",
            "tokenUrl": "https://app.botpaid.com/token",
            "scopes": {}
          }
        }
      }
    },
    "parameters": {
      "RequestId": {
        "name": "requestId",
        "in": "path",
        "required": true,
        "description": "Opaque Botpaid request identifier.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "XRateLimitLimit": {
        "description": "Maximum number of requests allowed in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitRemaining": {
        "description": "Number of requests remaining in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "XRateLimitReset": {
        "description": "Unix timestamp when the current rate-limit window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "Seconds until the client should retry.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "ErrorResponse": {
        "description": "Structured JSON error response.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimitedResponse": {
        "description": "The client has exceeded the rate limit for this route.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code."
              },
              "message": {
                "type": "string",
                "description": "Human-readable summary of the error."
              },
              "hint": {
                "type": "string",
                "description": "Recommended next step for the caller."
              },
              "docsUrl": {
                "type": "string",
                "format": "uri",
                "description": "Documentation page to resolve the error."
              },
              "retryAfterSeconds": {
                "type": "integer",
                "minimum": 1,
                "description": "Retry delay when applicable."
              }
            }
          }
        }
      },
      "McpJsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "description": "Client-provided request identifier.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ]
          },
          "method": {
            "type": "string"
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "McpJsonRpcBatch": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/McpJsonRpcRequest"
        },
        "minItems": 1
      },
      "PublicStatus": {
        "type": "object",
        "required": ["service", "status", "mcpEndpoint", "docs"],
        "properties": {
          "service": {
            "type": "string",
            "example": "botpaid-app"
          },
          "status": {
            "type": "string",
            "example": "ok"
          },
          "mcpEndpoint": {
            "type": "string",
            "format": "uri",
            "example": "https://app.botpaid.com/mcp"
          },
          "docs": {
            "type": "string",
            "format": "uri",
            "example": "https://botpaid.com/docs"
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "example": "https://botpaid.com/openapi.json"
          },
          "llms": {
            "type": "string",
            "format": "uri",
            "example": "https://botpaid.com/llms.txt"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GenerationStatus": {
        "type": "object",
        "required": ["type", "status", "createdAt", "elapsedSeconds", "estimatedSeconds"],
        "properties": {
          "type": {
            "type": "string",
            "example": "Video Generation"
          },
          "status": {
            "type": "string",
            "example": "processing"
          },
          "modelName": {
            "type": ["string", "null"]
          },
          "createdAt": {
            "type": "integer"
          },
          "elapsedSeconds": {
            "type": "integer"
          },
          "estimatedSeconds": {
            "type": "integer"
          },
          "completedAt": {
            "type": ["integer", "null"]
          }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string"
          },
          "provider_model_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "subtype": {
            "type": ["string", "null"]
          },
          "is_active": {
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "BalanceResponse": {
        "type": "object",
        "required": ["balance_cents", "balance_formatted"],
        "properties": {
          "balance_cents": {
            "type": "integer"
          },
          "balance_formatted": {
            "type": "string"
          },
          "refundable_cents": {
            "type": "integer"
          },
          "refundable_formatted": {
            "type": "string"
          },
          "non_refundable_cents": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "TransactionsResponse": {
        "type": "object",
        "required": ["transactions", "count"],
        "properties": {
          "transactions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "net_amount_cents": {
                  "type": "integer"
                },
                "balance_after_cents": {
                  "type": "integer"
                },
                "provider": {
                  "type": ["string", "null"]
                },
                "description": {
                  "type": "string"
                },
                "created_at": {
                  "type": "integer"
                }
              },
              "additionalProperties": true
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "CheckoutResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        },
        "additionalProperties": true
      },
      "WithdrawResponse": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "example": "preview"
          },
          "refundable_cents": {
            "type": "integer"
          },
          "refundable_formatted": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "total_refunded_cents": {
            "type": "integer"
          },
          "refund_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": true
      }
    }
  }
}