Build a Minimal Client
This walkthrough builds a small AIUS client from scratch: log in, mint an API
token, call the chat proxy, and (optionally) drive the run-loop WebSocket as a
tool-executor. The snippets are runnable end-to-end.
Targets production (https://aius.co/api). For dev, replace aius.co/api with
dev.aius.co/api and add Accept-Encoding: identity to HTTP requests.
The flow
Log in → session token
POST /v1/login returns a session_token (or a 2FA challenge to complete).
Mint an API token
POST /v1/tokens with the session cookie returns a durable aius_… token.
Call the chat proxy
POST /v1/chat/completions with Authorization: Bearer aius_….
(Optional) Drive the run loop
Connect to wss://…/v1/runs/ws, authenticate, start a run, execute tools.
Python
A single script: log in, mint a token, run a chat completion.
import httpx
BASE = "https://aius.co/api/v1"
EMAIL = "dev@example.com"
PASSWORD = "SuperSecret123!"
def get_session_token() -> str:
r = httpx.post(f"{BASE}/login", json={"email": EMAIL, "password": PASSWORD})
r.raise_for_status()
data = r.json()
if data.get("requires_2fa"):
code = input("2FA code: ").strip()
r = httpx.post(
f"{BASE}/2fa/login",
json={"challenge_token": data["challenge_token"], "code": code},
)
r.raise_for_status()
data = r.json()
return data["session_token"]
def mint_token(session_token: str) -> str:
r = httpx.post(
f"{BASE}/tokens",
json={"token_name": "minimal-client"},
cookies={"__Host-aius_session": session_token},
)
r.raise_for_status()
return r.json()["token"] # aius_...
def chat(api_token: str, prompt: str) -> str:
r = httpx.post(
f"{BASE}/chat/completions",
headers={"Authorization": f"Bearer {api_token}"},
json={
"model": "anthropic/claude-sonnet-4",
"messages": [{"role": "user", "content": prompt}],
},
timeout=60,
)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
if __name__ == "__main__":
session = get_session_token()
token = mint_token(session) # store this; shown only once
print(chat(token, "Give me a one-line haiku about data."))
Once you have a token, persist it (env var or a 0600 file) and skip the login +
mint steps on subsequent runs.
Driving the run loop (Python)
import asyncio, json, websockets
WS = "wss://aius.co/api/v1/runs/ws"
async def run(api_token: str, prompt: str):
async with websockets.connect(WS) as ws:
await ws.send(json.dumps({"type": "auth", "token": api_token}))
await ws.send(json.dumps({
"type": "start",
"agent": "build",
"model": "anthropic/claude-sonnet-4",
"messages": [{"role": "user", "content": prompt}],
}))
async for raw in ws:
f = json.loads(raw)
if f["type"] == "assistant_message":
print(f["message"].get("content"))
elif f["type"] == "tool_call":
args = json.loads(f.get("arguments") or "{}")
await ws.send(json.dumps({
"type": "tool_result",
"tool_call_id": f["tool_call_id"],
"status": "ok",
"result": run_tool(f["name"], args), # your tool impl
}))
elif f["type"] in ("run_completed", "error"):
break
def run_tool(name, args):
return f"(stub) ran {name} with {args}"
# asyncio.run(run("aius_xxxxxxxx...", "List files, then summarize."))
TypeScript
const BASE = "https://aius.co/api/v1";
const EMAIL = "dev@example.com";
const PASSWORD = "SuperSecret123!";
async function getSessionToken(): Promise<string> {
const r = await fetch(`${BASE}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
let data = await r.json();
if (data.requires_2fa) {
const code = "123456"; // prompt the user
const r2 = await fetch(`${BASE}/2fa/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ challenge_token: data.challenge_token, code }),
});
data = await r2.json();
}
return data.session_token;
}
async function mintToken(sessionToken: string): Promise<string> {
const r = await fetch(`${BASE}/tokens`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `__Host-aius_session=${sessionToken}`,
},
body: JSON.stringify({ token_name: "minimal-client" }),
});
return (await r.json()).token; // aius_...
}
async function chat(apiToken: string, prompt: string): Promise<string> {
const r = await fetch(`${BASE}/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "anthropic/claude-sonnet-4",
messages: [{ role: "user", content: prompt }],
}),
});
const data = await r.json();
return data.choices[0].message.content;
}
(async () => {
const session = await getSessionToken();
const token = await mintToken(session); // store this; shown once
console.log(await chat(token, "Give me a one-line haiku about data."));
})();
Driving the run loop (TypeScript)
See the full tool-executor example on the
Run-loop WebSocket page.
Next steps