LanguageEnglish

AI gateway

Use the AI accounts connected to your own AirCode Ø instance from your personal applications. Supply your instance’s Base URL, a separate application token, and an exact model ID. The instance invokes its connected provider CLI and returns the answer through a Chat Completions subset for text and an Images subset for pictures.

Your application's backend
  → your instance URL + application token
    → a connected native provider account
      → validated answer + available usage counters

The website hosts documentation only. API requests go directly to your instance; aircodezero.com does not receive your instance address, token or prompts. Use this service only for your own applications and accounts. Provider terms, subscription access and quotas still apply.

Set up the instance

  1. Connect a compatible native provider account in AI Usage. The gateway lists the account models supported by the installed instance; API-key and managed-credit connections are excluded.
  2. Open Settings → AI gateway on desktop/web, or More → AI gateway on native mobile. Under Create access, turn on Allow private connections.
  3. Copy the displayed Base URL. It ends in /api/ai-gateway/v1.
  4. In Create a token, choose An application, enter your application’s name, select Create a token, and save the token before dismissing its one-time reveal. The application choice grants text and image API access; it does not allow remote instance sessions.
  5. Configure these server-side environment variables in your application:
Variable Value
AIRCODE_AI_BASE_URL The full Base URL copied from the instance, without an extra /v1
AIRCODE_AI_TOKEN The application token you just created
AIRCODE_AI_MODEL An exact id from Compatible models or GET /models

Keep tokens in your backend’s secret configuration, not in browser code, a mobile app bundle, a repository, a URL or a shared conversation. The examples read environment variables instead of embedding credentials. Never use the instance password or an owner login token in the API-key field.

The instance must remain running and reachable from your application’s backend. localhost means the computer running that backend. Use the instance’s HTTPS address for remote access, or your trusted local/private-network connection. The gateway does not create a public origin. A provider not listed by /models cannot be selected by inventing an ID.

Authentication and model discovery

Send Authorization: Bearer YOUR_APPLICATION_TOKEN on each call. Send JSON with Content-Type: application/json. No query parameters or cookie authentication are supported. Cross-origin browser calls are not supported; use your backend.

GET {BASE_URL}/models returns an object with object: "list" and a data array. Each model provides an id, display label, engine, engineLabel, provider, connectionLabel, efforts and capabilities: chat.completions for text models, images.generations and images.edits for the image model. Pass the returned id unchanged; it may already contain percent encoding. The list can change when accounts, provider access or installed models change.

For an OpenAI-compatible application’s configuration, select Chat Completions, use the full Base URL, put the application token in its API key field, and copy one of the returned model IDs. Images use the Images subset described below. An application that requires Responses, tools or unsupported sampling parameters is not compatible with the current gateway.

Generate a response

POST {BASE_URL}/chat/completions accepts the following fields. Unknown fields are refused rather than silently ignored.

Field Supported value
model Required exact model ID returned by /models
messages Required array of 1–100 messages; at least one non-empty user message
messages[].role system, developer, user or assistant
messages[].content A string, or an array of { "type": "text", "text": "..." } blocks
reasoning_effort Optional value from the selected model’s efforts; omit it for the default
stream Optional boolean; defaults to false
stream_options Only { "include_usage": true } or false for that flag, with stream: true
n Omit it or use 1
store Omit it or use false

Messages contain only role and content. Text blocks are joined with newlines. Null characters are refused. The normalized message array must fit within 64 KiB of UTF-8 JSON; the full HTTP body is limited to 80 KiB. The validated answer is limited to 49,152 JavaScript string code units. These are gateway limits, not promises about a provider’s context window or output capacity.

Supply the complete relevant conversation on every call. To continue a conversation, append the previous assistant answer and the next user message. The gateway does not create or retain a conversation, session or completion history. A token grants no access to instance projects, files, terminal, sessions or owner settings.

A normal response has object: "chat.completion"; read the text from choices[0].message.content. It also includes id, created, the requested model, and finish_reason: "stop". Native counters appear in usage when complete enough to report. The aircode_usage extension preserves accounting details and incomplete estimates. Missing counters or prices are unknown, never zero. API-equivalent estimates are not extra subscription charges.

Streaming

Set stream: true to receive Content-Type: text/event-stream. The current implementation uses buffered streaming: it sends an initial waiting comment and keepalive comments while the provider runs, then sends the complete answer after validation. It does not stream words or tokens as they are generated.

A successful stream contains:

  1. SSE comments beginning with : while waiting; ignore them.
  2. A data: JSON chat.completion.chunk with the assistant role and complete text in choices[0].delta.content.
  3. A chunk with an empty delta and finish_reason: "stop".
  4. An optional chunk with choices: [], usage (possibly null) and aircode_usage when stream_options.include_usage is true.
  5. data: [DONE].

After a stream starts, failures arrive as an SSE data: event containing error; do not treat HTTP 200 alone as success. A failed stream closes without the success marker. SSE frames may be split across transport chunks; use an SDK or an SSE parser rather than parsing each network chunk as JSON.

Closing a connection cancels its active request. Revoking a token, disabling the gateway or stopping the instance also cancels matching work. There is no automatic replay or resume. Long-running requests need an appropriate client and reverse-proxy timeout; keepalives cannot override a network’s absolute request-duration limit.

Generate an image

The image model is listed while the instance’s native Codex account can use its own image tool, which requires a ChatGPT plan that includes image generation. Its id is codex/image-generation. Codex decides which image model it uses; the gateway does not choose one.

  • POST {BASE_URL}/images/generations takes JSON with model, prompt (up to 32,000 characters) and optionally size, n: 1 and response_format: "b64_json".
  • POST {BASE_URL}/images/edits takes multipart/form-data with the same fields and 1 to 4 reference images in image or image[]. Each must be a PNG or JPEG of at most 4 MiB. Other file fields, such as mask, are refused.

size is auto or WIDTHxHEIGHT between 64 and 4096 pixels. It is passed to the model as guidance; the response reports the actual size. Any other field is refused rather than ignored.

curl -sS "$AIRCODE_AI_BASE_URL/images/edits" \
  -H "Authorization: Bearer $AIRCODE_AI_TOKEN" \
  -F model=codex/image-generation \
  -F "prompt=Place this product in a premium studio shot on a stone pedestal." \
  -F size=1024x1536 \
  -F image=@product.png

A response carries created, one data[0].b64_json, output_format (png, jpeg or webp), the actual size and aircode_usage. The accounting covers the agent that drove the image tool; the provider does not report the image tool’s own cost, so costComplete is always false. A generation usually takes one to three minutes, shares the limits below with text calls and is cancelled when the client disconnects. When the model answers without an image, the call fails with 422 AI_GATEWAY_NO_IMAGE and the model’s sentence, if it gave one.

Connect a saved AirCode Ø app automatically

An app saved in Apps that runs its own server can receive this access without copying a token. On the app’s page, open Settings → Access → AI accounts and turn it on. AirCode Ø then:

  1. turns on the private AI gateway if it was off;
  2. restarts the app and, at every start, gives its server a fresh token of its own, revoking the token of the previous start;
  3. sets AIRCODE_AI_BASE_URL (the gateway on the address the instance listens on), AIRCODE_AI_TOKEN, and, when available, AIRCODE_AI_MODEL (a text model) and AIRCODE_AI_IMAGE_MODEL (the image model).

A published version of the app gets its own token in the same way. Turning AI accounts off revokes the app’s tokens immediately. Call the gateway from the app’s server and keep the token there: anyone who can open the app can use your accounts through it, and their usage counts toward your plans.

Quotas, token lifecycle and errors

The gateway allows two active calls instance-wide, one active call per token, and 30 admitted generation requests per token per minute. Calls are not queued. These local limits do not replace provider quotas. Tokens expire after 90 days; create a replacement, update your application, then revoke the previous token. Disabling the gateway retains tokens but suspends their use. Restoring instance state disables the gateway and clears token authority; create fresh tokens.

Errors use error.message, error.type, error.code and error.param. Read the code as well as the HTTP status. Do not automatically retry a generation whose outcome is unknown: it may already have consumed provider quota. The examples disable SDK retries.

Status/code What to do
401, AI_GATEWAY_AUTH_REQUIRED Check the token, expiry, revocation and target instance
403, AI_GATEWAY_DISABLED Enable the gateway on the target instance
403, AI_GATEWAY_ORIGIN_FORBIDDEN Call from your backend, not a foreign browser origin
AI_GATEWAY_ACCESS_REFUSED Check the instance’s access/subscription requirements
400, unsupported parameter or effort Remove unsupported fields; use the selected model’s advertised efforts
404/409, AI_GATEWAY_MODEL_UNAVAILABLE Refresh /models and explicitly select an available ID
413, AI_GATEWAY_INPUT_TOO_LARGE Reduce the supplied history, prompt or reference images; an oversized HTTP body is also refused
415 on /images/edits Send multipart/form-data
422, AI_GATEWAY_NO_IMAGE The model answered without an image; read its sentence and change the request
429, AI_GATEWAY_BUSY Wait for the active call to finish before trying again
429, AI_GATEWAY_RATE_LIMIT Stop immediate retries and wait for the local minute window
SAFE_STRUCTURED_AI_QUOTA_EXHAUSTED Wait for provider quota recovery; error.resets_at may be null
409, cancellation Treat the call as cancelled; do not resume automatically
502/503, provider/unavailable error Check the account connection and instance health before an explicit retry

An unknown provider reset time stays unknown. Never derive one from a model name or an unrelated provider’s quota schedule.

Using another instance from the application

An external application can call another own instance with that instance’s Base URL and a token created there. Each token belongs to its issuing instance. For an agent on B working on A’s project, choose Another instance when creating the token on B and follow Remote sessions. The default keeps A’s files and commands on A. Save that connection in Settings, then choose it under New session → Run on and keep Project location → This instance. Files and commands stay on A; B supplies the agent and account. Choosing This instance and a project while creating the token on B, then Project location → Remote instance at launch, uses that authorized project on B. Both modes send live events. Ordinary tokens cover text and images. Neither token signs the client in as the remote owner; Responses, Anthropic Messages and Chat Completions tool calling remain outside the text API’s supported subset.

For AI-assisted integration, see Documentation for AI, the raw Markdown guide, the OpenAPI schema, and the integration skill.

Code examples

Set the three environment variables above before running an example. These snippets are also available in Settings and in the raw Markdown guide.

cURL

Bash, curl and jq. List models first, then use the exact model ID you selected.

set -euo pipefail
# The token is read from standard input, not a command-line argument.
curl --fail-with-body --silent --show-error --location --max-redirs 0 \
  "$AIRCODE_AI_BASE_URL/models" --header @- <<EOF
Authorization: Bearer $AIRCODE_AI_TOKEN
EOF

body=$(jq -n --arg model "$AIRCODE_AI_MODEL" \
  '{model: $model, messages: [{role: "user", content: "Hello!"}]}')
curl --fail-with-body --silent --show-error --location --max-redirs 0 \
  "$AIRCODE_AI_BASE_URL/chat/completions" \
  --header 'Content-Type: application/json' \
  --header @- --data "$body" <<EOF
Authorization: Bearer $AIRCODE_AI_TOKEN
EOF

Python

Install the OpenAI Python SDK 3 with python -m pip install "openai>=3,<4".

import os
from openai import OpenAI, DefaultHttpx2Client

client = OpenAI(
    base_url=os.environ["AIRCODE_AI_BASE_URL"],
    api_key=os.environ["AIRCODE_AI_TOKEN"],
    max_retries=0,
    http_client=DefaultHttpx2Client(follow_redirects=False),
)

for model in client.models.list():
    print(model.id)

response = client.chat.completions.create(
    model=os.environ["AIRCODE_AI_MODEL"],
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)

JavaScript streaming

Node.js and the OpenAI SDK: npm install openai. This is buffered SSE: the complete validated answer arrives at the end.

import OpenAI from "openai";

function required(name) {
  if (!process.env[name]) throw new Error("Set " + name + " first.");
  return process.env[name];
}

const client = new OpenAI({
  baseURL: required("AIRCODE_AI_BASE_URL"),
  apiKey: required("AIRCODE_AI_TOKEN"),
  maxRetries: 0,
  fetchOptions: { redirect: "error" },
});

const stream = await client.chat.completions.create({
  model: required("AIRCODE_AI_MODEL"),
  messages: [{ role: "user", content: "Hello!" }],
  stream: true,
  stream_options: { include_usage: true },
});

try {
  for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
    if (chunk.usage) console.error("Tokens:", chunk.usage.total_tokens);
  }
  process.stdout.write("\n");
} finally {
  stream.controller.abort();
}

SDK configuration references:OpenAI Python SDKand OpenAI JavaScript SDK. This gateway supports the subset documented above.