{
  "openapi": "3.1.0",
  "info": {
    "title": "tokencoup Gateway API",
    "version": "0.3.0",
    "description": "OpenAI-compatible proxy in front of Baidu Qianfan. Edition and mode are encoded in the path. Pre-launch: production endpoint not yet live. v0.3 adds GET /v1/usage (local measured usage), GET /v1/models (upstream passthrough) and stream:true SSE (buffered passthrough).",
    "contact": {
      "name": "tokencoup docs",
      "url": "https://tokencoup.com/intl/en/docs.html"
    }
  },
  "servers": [
    {
      "url": "https://api.tokencoup.com",
      "description": "Planned production endpoint (not live yet)"
    },
    {
      "url": "http://127.0.0.1:8081",
      "description": "Local reference server (mock mode by default)"
    }
  ],
  "paths": {
    "/intl/a/v1/chat/completions": {
      "post": {
        "tags": [
          "INTL / Mode A"
        ],
        "summary": "intl Mode A — chat completions (Qianfan passthrough)",
        "description": "Bring your own Qianfan key; the key edition must match the serving edition. Set stream=true for a text/event-stream response.",
        "operationId": "intlAChatCompletions",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upstream Qianfan response, passed through unchanged. JSON by default; text/event-stream when stream=true.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE data: chunks terminated by `data: [DONE]`. v0.3 uses buffered passthrough (no chunk-by-chunk flush)."
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/intl/a/v1/usage": {
      "get": {
        "tags": [
          "INTL / Mode A"
        ],
        "summary": "Measured local usage — no upstream call, no billing",
        "description": "Values come from the gateway's subscriptions + usage_ledger tables and from gateway/plans.py. Authenticated and rate-limited, but never metered. Token counts are estimates (len(body)/4) until the upstream usage field is parsed.",
        "operationId": "intlAUsage",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Measured usage snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/intl/a/v1/models": {
      "get": {
        "tags": [
          "INTL / Mode A"
        ],
        "summary": "Upstream model catalogue passthrough",
        "description": "Relayed to the Qianfan upstream under the same edition/key checks. Verified 2026-11-12: the public endpoint returns HTTP 200 with 17 models, 14 carrying published per-token prices, without authentication. tokencoup does not rewrite the catalogue; a dated snapshot is published on the homepage. Directory prices are not a settlement commitment. Mock mode makes no network call and returns models: [].",
        "operationId": "intlAModels",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Upstream model list, passed through unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/intl/b/v1/chat/completions": {
      "post": {
        "tags": [
          "INTL / Mode B"
        ],
        "summary": "intl Mode B — chat completions (Qianfan passthrough)",
        "description": "Metered resale. No provider key required; international edition only. Set stream=true for a text/event-stream response.",
        "operationId": "intlBChatCompletions",
        "security": [
          {
            "GatewayKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upstream Qianfan response, passed through unchanged. JSON by default; text/event-stream when stream=true.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE data: chunks terminated by `data: [DONE]`. v0.3 uses buffered passthrough (no chunk-by-chunk flush)."
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/intl/b/v1/usage": {
      "get": {
        "tags": [
          "INTL / Mode B"
        ],
        "summary": "Measured local usage — no upstream call, no billing",
        "description": "Values come from the gateway's subscriptions + usage_ledger tables and from gateway/plans.py. Authenticated and rate-limited, but never metered. Token counts are estimates (len(body)/4) until the upstream usage field is parsed.",
        "operationId": "intlBUsage",
        "security": [
          {
            "GatewayKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Measured usage snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/intl/b/v1/models": {
      "get": {
        "tags": [
          "INTL / Mode B"
        ],
        "summary": "Upstream model catalogue passthrough",
        "description": "Relayed to the Qianfan upstream under the same edition/key checks. Verified 2026-11-12: the public endpoint returns HTTP 200 with 17 models, 14 carrying published per-token prices, without authentication. tokencoup does not rewrite the catalogue; a dated snapshot is published on the homepage. Directory prices are not a settlement commitment. Mock mode makes no network call and returns models: [].",
        "operationId": "intlBModels",
        "security": [
          {
            "GatewayKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Upstream model list, passed through unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cn/a/v1/chat/completions": {
      "post": {
        "tags": [
          "CN / Mode A"
        ],
        "summary": "cn Mode A — chat completions (Qianfan passthrough)",
        "description": "Bring your own Qianfan key; the key edition must match the serving edition. Set stream=true for a text/event-stream response.",
        "operationId": "cnAChatCompletions",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upstream Qianfan response, passed through unchanged. JSON by default; text/event-stream when stream=true.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "SSE data: chunks terminated by `data: [DONE]`. v0.3 uses buffered passthrough (no chunk-by-chunk flush)."
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cn/a/v1/usage": {
      "get": {
        "tags": [
          "CN / Mode A"
        ],
        "summary": "Measured local usage — no upstream call, no billing",
        "description": "Values come from the gateway's subscriptions + usage_ledger tables and from gateway/plans.py. Authenticated and rate-limited, but never metered. Token counts are estimates (len(body)/4) until the upstream usage field is parsed.",
        "operationId": "cnAUsage",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Measured usage snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/cn/a/v1/models": {
      "get": {
        "tags": [
          "CN / Mode A"
        ],
        "summary": "Upstream model catalogue passthrough",
        "description": "Relayed to the Qianfan upstream under the same edition/key checks. Verified 2026-11-12: the public endpoint returns HTTP 200 with 17 models, 14 carrying published per-token prices, without authentication. tokencoup does not rewrite the catalogue; a dated snapshot is published on the homepage. Directory prices are not a settlement commitment. Mock mode makes no network call and returns models: [].",
        "operationId": "cnAModels",
        "security": [
          {
            "GatewayKey": []
          },
          {
            "ProviderKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Upstream model list, passed through unchanged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "edition_unknown — missing or invalid edition/mode path prefix",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "gateway_auth_failed — missing, invalid or revoked X-Gateway-Key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "quota_exceeded — Mode B token quota exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "mode_B_not_allowed_on_cn / missing_provider_key / key_edition_unknown / key_edition_mismatch",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "unknown_endpoint — GET path is not /v1/usage or /v1/models",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "rate_limited — plan RPM exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "resale_pool_empty — international resale key pool not configured",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "GatewayKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Gateway-Key",
        "description": "tokencoup gateway API key (rf_…). Required on every request."
      },
      "ProviderKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your own Qianfan key. Mode A only; edition must match."
      }
    },
    "schemas": {
      "ChatRequest": {
        "type": "object",
        "required": [
          "model",
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string",
            "example": "ernie-4.0-8k"
          },
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "role",
                "content"
              ],
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "system",
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "type": "string"
                }
              }
            }
          },
          "temperature": {
            "type": "number",
            "example": 0.7
          },
          "stream": {
            "type": "boolean",
            "example": false,
            "description": "true returns text/event-stream. v0.3 uses buffered passthrough; chunk-by-chunk flush is not implemented."
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "key_id",
          "plan",
          "token_quota",
          "token_used_billable",
          "token_remaining",
          "rpm_limit",
          "ledger_calls",
          "ledger_tokens",
          "billed_amount_usd"
        ],
        "properties": {
          "key_id": {
            "type": "string",
            "example": "k_1a2b3c4d"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "pro",
              "scale"
            ]
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "token_quota": {
            "type": "integer",
            "example": 5000000
          },
          "token_used_billable": {
            "type": "integer",
            "description": "Mode B billable tokens only — not equal to ledger_tokens"
          },
          "token_remaining": {
            "type": "integer"
          },
          "rpm_limit": {
            "type": "integer",
            "example": 600
          },
          "ledger_calls": {
            "type": "integer",
            "description": "All recorded calls (Mode A and Mode B)"
          },
          "ledger_tokens": {
            "type": "integer",
            "description": "Estimated tokens across all calls (len(body)/4)"
          },
          "billed_amount_usd": {
            "type": "number"
          },
          "overage_usd_per_1k_tokens": {
            "type": "number",
            "example": 0.002
          },
          "source": {
            "type": "string",
            "example": "local subscriptions + usage_ledger (measured, not estimated)"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "example": "quota_exceeded"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "INTL / Mode A"
    },
    {
      "name": "INTL / Mode B"
    },
    {
      "name": "CN / Mode A"
    }
  ]
}
