{
  "openapi": "3.1.0",
  "info": {
    "title": "FreeTheAI Gateway API",
    "version": "1.0.0",
    "description": "One marketplace gateway with OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages wire formats. Authenticate with an API key created in the console; the routing rule on the key selects a provider per request. Use the exact model IDs from GET /v1/models: free FreeTheAI models are named fta/\u003cprovider\u003e/\u003cmodel\u003e and need a verified email and the daily check-in on the website. Streaming responses include token usage in the final chunk."
  },
  "servers": [
    {
      "url": "/",
      "description": "Gateway"
    }
  ],
  "paths": {
    "/v1/chat/completions": {
      "post": {
        "operationId": "createChatCompletions",
        "summary": "Create a chat completion",
        "description": "Routes the OpenAI Chat Completions wire format to the provider selected by the API key.",
        "tags": [
          "Inference"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatCompletionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful provider response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletionResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is invalid, uses a parameter the model does not support, or calls an endpoint the model does not serve. When the provider itself refused the request, the error carries an error ID (in error.error_id and the X-Error-ID header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked, or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Your wallet balance cannot cover this paid route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request is not allowed: your email is not verified, free models need today's check-in, your account is on hold, your key does not allow this model, provider, or IP address, your network or client is blocked, or the request was relayed through a Cloudflare Worker (call the API directly).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The model does not exist or has no available route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The request body is larger than the gateway accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A rate limit, concurrency limit, or your daily free request limit was reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The upstream provider failed, or the stream broke after it started. The error ID (such as freetheai-1a2b3c4d5e6f) is at the end of the message, in error.error_id, and in the X-Error-ID header; a broken stream ends with an error event that carries it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The model is at capacity or the service is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Service health",
        "tags": [
          "Service"
        ],
        "responses": {
          "200": {
            "description": "Service is reachable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "operationId": "createMessages",
        "summary": "Create an Anthropic message",
        "description": "Routes the Anthropic Messages wire format to the provider selected by the API key. Send the Anthropic version header required by your client.",
        "tags": [
          "Inference"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful provider response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagesResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is invalid, uses a parameter the model does not support, or calls an endpoint the model does not serve. When the provider itself refused the request, the error carries an error ID (in error.error_id and the X-Error-ID header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked, or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Your wallet balance cannot cover this paid route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request is not allowed: your email is not verified, free models need today's check-in, your account is on hold, your key does not allow this model, provider, or IP address, your network or client is blocked, or the request was relayed through a Cloudflare Worker (call the API directly).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The model does not exist or has no available route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The request body is larger than the gateway accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A rate limit, concurrency limit, or your daily free request limit was reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The upstream provider failed, or the stream broke after it started. The error ID (such as freetheai-1a2b3c4d5e6f) is at the end of the message, in error.error_id, and in the X-Error-ID header; a broken stream ends with an error event that carries it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The model is at capacity or the service is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/models": {
      "get": {
        "operationId": "listModels",
        "summary": "List available models",
        "description": "Returns first-party and marketplace models the calling key may reach. Per-key allowlists filter this listing; group and scope limits stay enforced at dispatch.",
        "tags": [
          "Models"
        ],
        "responses": {
          "200": {
            "description": "Model list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelList"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/responses": {
      "post": {
        "operationId": "createResponses",
        "summary": "Create a model response",
        "description": "Routes the OpenAI Responses wire format to the provider selected by the API key.",
        "tags": [
          "Inference"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResponsesRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful provider response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResponsesResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is invalid, uses a parameter the model does not support, or calls an endpoint the model does not serve. When the provider itself refused the request, the error carries an error ID (in error.error_id and the X-Error-ID header).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed, revoked, or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Your wallet balance cannot cover this paid route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The request is not allowed: your email is not verified, free models need today's check-in, your account is on hold, your key does not allow this model, provider, or IP address, your network or client is blocked, or the request was relayed through a Cloudflare Worker (call the API directly).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "The model does not exist or has no available route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The request body is larger than the gateway accepts.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "A rate limit, concurrency limit, or your daily free request limit was reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "The upstream provider failed, or the stream broke after it started. The error ID (such as freetheai-1a2b3c4d5e6f) is at the end of the message, in error.error_id, and in the X-Error-ID header; a broken stream ends with an error event that carries it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The model is at capacity or the service is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ChatCompletionRequest": {
        "type": "object",
        "description": "OpenAI Chat Completions request. Unknown OpenAI-compatible fields are forwarded when supported by the selected route.",
        "properties": {
          "max_tokens": {
            "type": "integer",
            "description": "Maximum generated tokens when supported.",
            "format": "int64"
          },
          "messages": {
            "type": "array",
            "description": "Conversation messages.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "model": {
            "type": "string",
            "description": "Model id from GET /v1/models."
          },
          "stream": {
            "type": "boolean",
            "description": "Return server-sent events when true."
          }
        },
        "required": [
          "model",
          "messages"
        ],
        "additionalProperties": true
      },
      "ChatCompletionResponse": {
        "type": "object",
        "description": "OpenAI Chat Completions response or stream chunks when stream is true.",
        "properties": {
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "example": "chat.completion"
          },
          "usage": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "message": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "example": "gateway_error"
              }
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          }
        }
      },
      "MessagesRequest": {
        "type": "object",
        "description": "Anthropic Messages request. Unknown Anthropic-compatible fields are forwarded when supported by the selected route.",
        "properties": {
          "max_tokens": {
            "type": "integer",
            "description": "Maximum generated tokens.",
            "format": "int64"
          },
          "messages": {
            "type": "array",
            "description": "Anthropic-format conversation messages.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "model": {
            "type": "string",
            "description": "Model id from GET /v1/models."
          },
          "stream": {
            "type": "boolean",
            "description": "Return Anthropic server-sent events when true."
          },
          "system": {
            "description": "System prompt as a string or Anthropic content blocks."
          }
        },
        "required": [
          "model",
          "max_tokens",
          "messages"
        ],
        "additionalProperties": true
      },
      "MessagesResponse": {
        "type": "object",
        "description": "Anthropic Messages response or stream events when stream is true.",
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "example": "assistant"
          },
          "stop_reason": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "example": "message"
          },
          "usage": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "Model": {
        "type": "object",
        "properties": {
          "created": {
            "type": "integer",
            "format": "int64"
          },
          "id": {
            "type": "string",
            "description": "Model identifier to send as `model`."
          },
          "object": {
            "type": "string",
            "example": "model"
          },
          "owned_by": {
            "type": "string",
            "description": "Owning provider as shown in the marketplace."
          }
        },
        "required": [
          "id",
          "object",
          "created",
          "owned_by"
        ]
      },
      "ModelList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          },
          "object": {
            "type": "string",
            "example": "list"
          }
        },
        "required": [
          "object",
          "data"
        ]
      },
      "ResponsesRequest": {
        "type": "object",
        "description": "OpenAI Responses request. Unknown Responses fields are forwarded when supported by the selected route.",
        "properties": {
          "input": {
            "description": "String or structured Responses input."
          },
          "instructions": {
            "type": "string"
          },
          "model": {
            "type": "string",
            "description": "Model id from GET /v1/models."
          },
          "stream": {
            "type": "boolean",
            "description": "Return server-sent events when true."
          }
        },
        "required": [
          "model"
        ],
        "additionalProperties": true
      },
      "ResponsesResponse": {
        "type": "object",
        "description": "OpenAI Responses response or stream events when stream is true.",
        "properties": {
          "id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "example": "response"
          },
          "output": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "usage": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API key",
        "description": "Create a key in the console. Each key carries its own routing rule, spend limit, and wallet."
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Inference",
      "description": "OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages."
    },
    {
      "name": "Models",
      "description": "Model discovery."
    },
    {
      "name": "Service",
      "description": "Health and status."
    }
  ]
}