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 text-only Chat Completions API.
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
- 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.
- Open Settings → AI gateway on desktop/web, or More → AI gateway on native mobile, then turn on Enable AI gateway.
- Copy the displayed Base URL. It ends in
/api/ai-gateway/v1. - In Application tokens, enter your application’s name, select Create token, and save the token before dismissing its one-time reveal.
- 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 and efforts. 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. An application that requires Responses, tools, images 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:
- SSE comments beginning with
:while waiting; ignore them. - A
data:JSONchat.completion.chunkwith the assistant role and complete text inchoices[0].delta.content. - A chunk with an empty delta and
finish_reason: "stop". - An optional chunk with
choices: [],usage(possiblynull) andaircode_usagewhenstream_options.include_usageis true. 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.
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; an oversized HTTP body is also refused |
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. The gateway currently supplies text completions only. It is not yet an AirCode Ø engine connection for interactive coding sessions: it does not implement Responses, Anthropic Messages or tool calling. A gateway token also cannot log the desktop/mobile client into another instance as its owner.
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
EOFPython
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.