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

1

Log in → session token

POST /v1/login returns a session_token (or a 2FA challenge to complete).
2

Mint an API token

POST /v1/tokens with the session cookie returns a durable aius_… token.
3

Call the chat proxy

POST /v1/chat/completions with Authorization: Bearer aius_….
4

(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