{
  "openapi": "3.1.0",
  "info": {
    "title": "SUB&SUB API",
    "version": "2026-07-14",
    "description": "Stable public contract for the SUB&SUB multi-provider relay. API onboarding is currently limited to accounts created before 2026-06-08 00:00 Beijing time. Fields outside this document may be passed through to an upstream provider but are not guaranteed by SUB&SUB.",
    "contact": {
      "name": "SUB&SUB Support",
      "email": "support@subnsub.com"
    },
    "license": {
      "name": "Proprietary — use subject to the SUB&SUB Terms of Service",
      "url": "https://www.subnsub.com/terms"
    }
  },
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://www.subnsub.com/docs"
  },
  "servers": [
    {
      "url": "https://api.subnsub.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "OpenAI-compatible",
      "description": "OpenAI-shaped text-generation routes served through the SUB&SUB relay."
    },
    {
      "name": "Anthropic-native",
      "description": "Anthropic Messages-protocol generation and token counting."
    },
    {
      "name": "Discovery",
      "description": "Runtime discovery of the currently callable public model catalogue."
    }
  ],
  "paths": {
    "/v1/chat/completions": {
      "post": {
        "tags": ["OpenAI-compatible"],
        "summary": "Create a chat completion",
        "description": "OpenAI-compatible text generation. Set stream=true for Server-Sent Events. Additional fields are forwarded without becoming part of the stable contract.",
        "operationId": "createChatCompletion",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ChatCompletionRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completion JSON, or an SSE stream when stream=true.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ProviderResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "400": { "$ref": "#/components/responses/OpenAIClientError" },
          "401": { "$ref": "#/components/responses/OpenAIAuthError" },
          "402": { "$ref": "#/components/responses/OpenAIBalanceError" },
          "403": { "$ref": "#/components/responses/OpenAIPermissionError" },
          "429": { "$ref": "#/components/responses/OpenAIRateLimitError" },
          "500": { "$ref": "#/components/responses/OpenAIServerError" },
          "502": { "$ref": "#/components/responses/OpenAIServerError" },
          "503": { "$ref": "#/components/responses/OpenAIServerError" }
        }
      }
    },
    "/v1/responses": {
      "post": {
        "tags": ["OpenAI-compatible"],
        "summary": "Create an OpenAI-style response",
        "description": "Synchronous or streaming Responses API. Background mode is not supported.",
        "operationId": "createResponse",
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ResponsesRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response JSON, or an SSE event sequence when stream=true.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ProviderResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "400": { "$ref": "#/components/responses/OpenAIClientError" },
          "401": { "$ref": "#/components/responses/OpenAIAuthError" },
          "402": { "$ref": "#/components/responses/OpenAIBalanceError" },
          "403": { "$ref": "#/components/responses/OpenAIPermissionError" },
          "429": { "$ref": "#/components/responses/OpenAIRateLimitError" },
          "500": { "$ref": "#/components/responses/OpenAIServerError" },
          "502": { "$ref": "#/components/responses/OpenAIServerError" },
          "503": { "$ref": "#/components/responses/OpenAIServerError" }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "tags": ["Anthropic-native"],
        "summary": "Create an Anthropic message",
        "description": "Anthropic-native generation for claude-* models. Send anthropic-version: 2023-06-01. Set stream=true for Anthropic SSE events.",
        "operationId": "createMessage",
        "security": [{ "anthropicApiKey": [] }, { "bearerAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/AnthropicVersion" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MessageRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Message JSON, or an Anthropic SSE event sequence when stream=true.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/ProviderResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "400": { "$ref": "#/components/responses/AnthropicClientError" },
          "401": { "$ref": "#/components/responses/AnthropicAuthError" },
          "402": { "$ref": "#/components/responses/AnthropicPermissionError" },
          "403": { "$ref": "#/components/responses/AnthropicPermissionError" },
          "429": { "$ref": "#/components/responses/AnthropicRateLimitError" },
          "500": { "$ref": "#/components/responses/AnthropicServerError" },
          "502": { "$ref": "#/components/responses/AnthropicServerError" },
          "503": { "$ref": "#/components/responses/AnthropicServerError" }
        }
      }
    },
    "/v1/messages/count_tokens": {
      "post": {
        "tags": ["Anthropic-native"],
        "summary": "Count tokens in an Anthropic prompt",
        "description": "Uses the Anthropic request shape and is not billed. The :online suffix is stripped without fetching or counting search results.",
        "operationId": "countMessageTokens",
        "security": [{ "anthropicApiKey": [] }, { "bearerAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/AnthropicVersion" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CountTokensRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token count.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["input_tokens"],
                  "properties": { "input_tokens": { "type": "integer", "minimum": 0 } },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/AnthropicClientError" },
          "401": { "$ref": "#/components/responses/AnthropicAuthError" },
          "402": { "$ref": "#/components/responses/AnthropicPermissionError" },
          "403": { "$ref": "#/components/responses/AnthropicPermissionError" },
          "429": { "$ref": "#/components/responses/AnthropicRateLimitError" },
          "500": { "$ref": "#/components/responses/AnthropicServerError" },
          "502": { "$ref": "#/components/responses/AnthropicServerError" },
          "503": { "$ref": "#/components/responses/AnthropicServerError" }
        }
      }
    },
    "/v1/models": {
      "get": {
        "tags": ["Discovery"],
        "summary": "List currently callable public models",
        "description": "Returns the verified public model catalogue. The exact list is runtime data and is intentionally not duplicated as an enum in this document.",
        "operationId": "listModels",
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Model list.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ModelList" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/OpenAIAuthError" },
          "402": { "$ref": "#/components/responses/OpenAIBalanceError" },
          "403": { "$ref": "#/components/responses/OpenAIPermissionError" },
          "500": { "$ref": "#/components/responses/OpenAIServerError" },
          "502": { "$ref": "#/components/responses/OpenAIServerError" },
          "503": { "$ref": "#/components/responses/OpenAIServerError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk-cf-...",
        "description": "API key created in the SUB&SUB console."
      },
      "anthropicApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "The same sk-cf-... key, in Anthropic SDK format."
      }
    },
    "parameters": {
      "AnthropicVersion": {
        "name": "anthropic-version",
        "in": "header",
        "required": true,
        "schema": { "type": "string", "default": "2023-06-01" },
        "description": "Anthropic protocol version. SDKs set this automatically."
      }
    },
    "schemas": {
      "ReasoningEffort": {
        "type": "string",
        "enum": ["none", "low", "medium", "high", "xhigh"]
      },
      "ChatMessage": {
        "type": "object",
        "required": ["role", "content"],
        "properties": {
          "role": { "type": "string", "enum": ["system", "user", "assistant", "tool"] },
          "content": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": {} }
            ]
          }
        },
        "additionalProperties": true
      },
      "ChatCompletionRequest": {
        "type": "object",
        "required": ["model", "messages"],
        "properties": {
          "model": { "type": "string", "description": "Use GET /v1/models. Append :online to enable web search." },
          "messages": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/ChatMessage" } },
          "stream": { "type": "boolean", "default": false },
          "stream_options": { "type": "object", "additionalProperties": true },
          "max_tokens": { "type": "integer", "minimum": 1 },
          "temperature": { "type": "number", "minimum": 0, "maximum": 2 },
          "reasoning_effort": { "$ref": "#/components/schemas/ReasoningEffort" }
        },
        "additionalProperties": true
      },
      "ResponsesRequest": {
        "type": "object",
        "required": ["model", "input"],
        "properties": {
          "model": { "type": "string", "description": "Use GET /v1/models. :online is not supported on this route." },
          "input": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": {} }
            ]
          },
          "stream": { "type": "boolean", "default": false },
          "background": { "type": "boolean", "const": false, "default": false },
          "max_output_tokens": { "type": "integer", "minimum": 1 },
          "reasoning": {
            "type": "object",
            "properties": { "effort": { "$ref": "#/components/schemas/ReasoningEffort" } },
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "AnthropicMessage": {
        "type": "object",
        "required": ["role", "content"],
        "properties": {
          "role": { "type": "string", "enum": ["user", "assistant"] },
          "content": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": {} }
            ]
          }
        },
        "additionalProperties": true
      },
      "MessageRequest": {
        "type": "object",
        "required": ["model", "max_tokens", "messages"],
        "properties": {
          "model": { "type": "string", "pattern": "^claude-", "description": "Use GET /v1/models. Append :online to enable web search." },
          "max_tokens": { "type": "integer", "minimum": 1 },
          "messages": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/AnthropicMessage" } },
          "system": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": {} }
            ]
          },
          "stream": { "type": "boolean", "default": false },
          "thinking": { "type": "object", "additionalProperties": true },
          "output_config": { "type": "object", "additionalProperties": true },
          "tools": { "type": "array", "items": {} }
        },
        "additionalProperties": true
      },
      "CountTokensRequest": {
        "type": "object",
        "required": ["model", "messages"],
        "properties": {
          "model": { "type": "string", "pattern": "^claude-" },
          "messages": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/AnthropicMessage" } },
          "system": {
            "oneOf": [
              { "type": "string" },
              { "type": "array", "items": {} }
            ]
          },
          "tools": { "type": "array", "items": {} }
        },
        "additionalProperties": true
      },
      "ProviderResponse": {
        "type": "object",
        "description": "Provider-protocol response. Exact fields depend on endpoint and model.",
        "additionalProperties": true
      },
      "OpenAIError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["message", "type"],
            "properties": {
              "message": { "type": "string" },
              "type": { "type": "string" },
              "code": { "type": ["string", "null"] }
            },
            "additionalProperties": true
          }
        }
      },
      "AnthropicError": {
        "type": "object",
        "required": ["type", "error"],
        "properties": {
          "type": { "type": "string", "const": "error" },
          "error": {
            "type": "object",
            "required": ["type", "message"],
            "properties": {
              "type": { "type": "string" },
              "message": { "type": "string" }
            },
            "additionalProperties": true
          }
        }
      },
      "ModelList": {
        "type": "object",
        "required": ["object", "data"],
        "properties": {
          "object": { "type": "string", "const": "list" },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["id", "object", "owned_by"],
              "properties": {
                "id": { "type": "string" },
                "object": { "type": "string", "const": "model" },
                "owned_by": { "type": "string", "enum": ["openai", "anthropic"] }
              },
              "additionalProperties": true
            }
          }
        }
      }
    },
    "responses": {
      "OpenAIClientError": {
        "description": "Invalid request or unavailable model.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "OpenAIAuthError": {
        "description": "Missing or unknown API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "OpenAIBalanceError": {
        "description": "Account balance is negative.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "OpenAIPermissionError": {
        "description": "Revoked key or account without API access.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "OpenAIRateLimitError": {
        "description": "Upstream rate limit. Honour Retry-After when present.",
        "headers": { "Retry-After": { "schema": { "type": "string" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "OpenAIServerError": {
        "description": "Relay or upstream failure.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OpenAIError" } } }
      },
      "AnthropicClientError": {
        "description": "Invalid Anthropic request or unavailable model.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnthropicError" } } }
      },
      "AnthropicAuthError": {
        "description": "Missing or unknown API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnthropicError" } } }
      },
      "AnthropicPermissionError": {
        "description": "Negative balance, revoked key, or account without API access.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnthropicError" } } }
      },
      "AnthropicRateLimitError": {
        "description": "Upstream rate limit. Honour Retry-After when present.",
        "headers": { "Retry-After": { "schema": { "type": "string" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnthropicError" } } }
      },
      "AnthropicServerError": {
        "description": "Relay or upstream failure.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnthropicError" } } }
      }
    }
  }
}
