Downcity
Downcity City DocsReference

HTTP API

Auth, requests, and responses under the unified `/v1/*` route space.

All actions are exposed through the same shape:

POST /v1/{service}/{action}
GET  /v1/{service}/{action}

The bearer credential decides the request identity:

  • no token: guest
  • user_token: user
  • admin_secret_key: admin

Each action declares which identities it allows. The default is ["user"]; [] means guest access is allowed.

Studio Routes (common user-side routes)

RouteMethodPurposePathway
/v1/ai/modelsGETModel catalog
/v1/ai/textPOSTText generationSDK
/v1/ai/streamPOSTStreaming generationSDK
/v1/ai/imagePOSTImage generationSDK
/v1/ai/videoPOSTVideo generationSDK
/v1/ai/ttsPOSTText-to-speechSDK
/v1/ai/asrPOSTSpeech recognitionSDK
/v1/ai/chat/completionsPOSTOpenAI-compatible endpointOpenAI
/v1/accounts/loginPOSTAccounts login
/v1/accounts/oauth/startPOSTStart OAuth
/v1/accounts/oauth/resultGETPoll OAuth result
/v1/accounts/oauth/callbackGETThird-party OAuth callback entry
/v1/servicesGETRegistered service list
/v1/{service}/{action}POSTGeneric action call

/v1/accounts/oauth/callback is the fixed return URL for GitHub, Google, and other third-party OAuth providers. Studio clients usually do not call it directly.

Admin Routes (same route space, admin credential required)

RouteMethodPurpose
/v1/studios/listGETList studios
/v1/studios/createPOSTCreate studio
/v1/studios/pausePOSTPause studio
/v1/studios/activatePOSTActivate studio
/v1/studios/removePOSTRemove studio
/v1/studios/tokens/applyPOSTIssue user_token
/v1/env/listGETView runtime env
/v1/env/upsertPOSTWrite env variable
/v1/env/removePOSTDelete env variable
/v1/env/importPOSTBulk import .env

Auth Header

Authorization: Bearer <user_token>
Content-Type: application/json

GET /v1/ai/models

The same route returns different visibility by identity:

  • with user_token: only currently callable models
  • with admin_secret_key: the full code-registered model list plus env_requirements
{ "items": [
  { "id": "deepseek-v4-flash", "name": "DeepSeek V4 Flash",
    "description": "...", "modalities": ["text","stream"],
    "tags": ["deepseek"], "meta": {} }
]}
{ "items": [
  { "id": "deepseek-v4-flash", "name": "DeepSeek V4 Flash",
    "description": "", "modalities": ["text","stream","openai"],
    "tags": ["deepseek"], "meta": {},
    "env_requirements": [
      { "key": "DEEPSEEK_API_KEY", "description": "deepseek API Key", "required": true }
    ],
    "default_modes": ["text","stream","openai"] }
]}

POST /v1/ai/text

Request:

{ "studio_id": "studio_xxx", "model": "deepseek-v4-flash", "prompt": "Hello" }

Response:

{ "id": "msg_xxx", "role": "assistant", "parts": [{ "type": "text", "text": "Hi!" }] }

If the request body includes studio_id, it must match the studio bound to the user_token. Otherwise City returns 403.

GET /v1/services

{
  "items": [
    { "id": "ai", "name": "AI", "env": [] },
    { "id": "accounts", "name": "Accounts", "env": [] }
  ]
}

env exposes the runtime env requirements declared by the service so admin or debugging surfaces can render dependencies.

POST /v1/ai/chat/completions

OpenAI-compatible endpoint. Accepts standard { model, messages, stream } format.

Non-streaming returns OpenAI chat.completion JSON. Streaming returns OpenAI SSE.

Fully compatible with OpenAI SDK:

  const openai = new OpenAI({
  baseURL: "http://127.0.0.1:43127/v1/ai",
  apiKey: "ub_xxx",
});