Provider

Ten models. One endpoint. Yours.

ZYRAXON serves its own line of ten branded models over a single OpenAI-compatible API, and rotates the pool behind those names so your code never has to change when an upstream model does. Chat and streaming, the model catalogue, text to speech and speech to text — 7 endpoints, no key to buy, and every one of them described in full on this page.

The functionsThe modelsQuickstartUse it anywhereOpenAI-compatibleStreamingSpeech, both waysNo key

In short

Speech in, speech out, and any media in between

Three questions the provider gets asked first, each with a one-line answer and a page of its own.

🎙Speech to text

Putting a microphone in your chat box? Use this endpoint.

You send real-time audio, we send real-time text back. POST /api/public/stt takes either a whole recording and returns one transcript, or raw PCM streamed live while the person is still speaking — partial text arrives as they talk. It is the same URL for both, and the request itself picks the mode.

Live SSEBatchUp to 100 MBLanguage is required
https://zyraxonai.lovable.app/api/public/stt
Open Speech to text →
🔊Text to speech

Send us text, we send you audio.

POST /api/public/tts turns an answer into speech and streams the MP3 back as it is synthesised. The voice follows the language of the text, so a Bengali answer is read by a Bengali voice rather than an English one sounding out Bengali letters. 322 voices across 75 languages, and no key.

MP3 stream322 voices75 languages
https://zyraxonai.lovable.app/api/public/tts
Open Text to speech →
▣Chat with any media

Video, audio, pictures — every model takes them all.

The chat endpoint accepts the OpenAI content-parts array, so one message can mix text with media. Send each part inline as base64 or as an https:// URL and we fetch it server-side. A turn that carries media is routed to a model that can actually read it, so a video is never handed to a text-only model and made to fail.

ImageVideoAudioPDFInline or URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions
Open Chat with any media →

The functions

15 functions, one base URL

Each of these is a page of its own. Open one to see just that function — its endpoint, its fields, its code — instead of a wall of prose that mentions everything.

Base URL

https://zyraxonai.lovable.app/api/public/zyraxon/v1
💬Chat completionsThe one call your app makesOpen →

OpenAI chat-completions semantics — messages in, a reply or a stream of chunks out — with optional media and tools. This is the call behind every integration on this site.

POSThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions

The call your app makes. OpenAI chat-completions semantics, with server-sent-event streaming when you ask for it.

Returns choices[].message.content, or a stream of chat.completion.chunk objects when stream is true.

FieldRequiredWhat it does
modelyesAny id from the model catalogue, e.g. zyraxon-apex.
messagesyesThe usual [{role, content}] array.
streamnotrue for server-sent events.
toolsnoOpenAI function-tool definitions; the model calls them.
Streamingshell
curl -sN https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "stream": true,
    "messages": [{ "role": "user", "content": "Hello" }]
  }'
1 more example
Non-streamingshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }]
  }' | jq -r '.choices[0].message.content'
  • The model value is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
◎The model lineTen names you controlOpen →

Ten ZYRAXON branded models behind one endpoint, with a pool of upstream models rotating underneath each. Read the catalogue to discover them, or the provider endpoint for their live health.

GEThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/models

What a client reads first, to discover which models exist. Standard OpenAI list shape, so no special handling is needed.

Returns { object: "list", data: [{ id, owned_by, … }] } — ten ZYRAXON models.

List the lineshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[] | .id'
▣Images, audio, video and PDFSend any modality to any modelOpen →

Every ZYRAXON model takes every modality. Put an image, a recording, a video or a document in a message and the turn is routed to a model that can actually read it — never a text-only model made to fail.

POSThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions

The call your app makes. OpenAI chat-completions semantics, with server-sent-event streaming when you ask for it.

Returns choices[].message.content, or a stream of chat.completion.chunk objects when stream is true.

FieldRequiredWhat it does
modelyesAny id from the model catalogue, e.g. zyraxon-apex.
messagesyesThe usual [{role, content}] array.
streamnotrue for server-sent events.
toolsnoOpenAI function-tool definitions; the model calls them.
Streamingshell
curl -sN https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "stream": true,
    "messages": [{ "role": "user", "content": "Hello" }]
  }'
1 more example
Non-streamingshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }]
  }' | jq -r '.choices[0].message.content'
  • The model value is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
🎙Speech to textA microphone for your chat boxOpen →

If you are putting a microphone in your chat box, use this endpoint: you send real-time audio and we send real-time text back. One URL, two modes — a whole recording for one transcript, or raw PCM streamed live as the person is still speaking, with partial text arriving as they talk.

POSThttps://zyraxonai.lovable.app/api/public/stt

Send a recording and get the transcript, or stream audio while the person is still speaking and get text as it is recognised. Same URL; the request itself picks the mode.

Returns Batch: { success, text, language, confidence, engine, container, duration, elapsed }. Live: server-sent events — ready, language, partial, segment, done.

FieldRequiredWhat it does
languageyesbn or bn-BD. Required in both modes — the recogniser does not detect language, so a guess would be a wrong transcript.
filenoBatch, multipart: the audio. Format is read from the bytes, so a mislabelled file still works.
audionoBatch, JSON: base64 audio, with an optional format.
segmentnoLive: seconds per committed chunk, default 4.
partialnoLive: seconds between partial updates, default 1.2.
A recordingshell
# A recording you already have. The format is read from the bytes,
# so a mislabelled file still works. language is required — the
# recogniser does not detect it, and a guess is a wrong transcript.
curl -s https://zyraxonai.lovable.app/api/public/stt \
  -F "file=@recording.mp3" \
  -F "language=bn" \
| jq -r '.text'
5 more examples
Live, from a pipeshell
# Live transcription over Server-Sent Events.
# Body: raw 16-bit mono PCM at 16 kHz — one second is 32000 bytes.
# --data-binary with @- streams stdin, so this works from a pipe:
ffmpeg -i recording.mp3 -ar 16000 -ac 1 -f s16le - \
| curl -sN -X POST "https://zyraxonai.lovable.app/api/public/stt?language=bn" \
    -H "Content-Type: audio/l16; rate=16000" \
    -H "Accept: text/event-stream" \
    --data-binary @-

# event: ready     {"rate":16000,"language":"bn"}
# event: partial   {"text":"আজ আকাশ"}
# event: partial   {"text":"আজ আকাশ পরিষ্কার"}
# event: segment   {"text":"আজ আকাশ পরিষ্কার এবং সূর্য উজ্জ্বল।","seconds":4.2}
# event: done      {"text":"...","segments":2,"seconds":8.1}
Live, in a browsertypescript
// Live transcription from a browser microphone.
//
// The browser records to WebM/Opus, which cannot be cut mid-frame, so the audio
// is resampled to raw PCM here with an AudioWorklet-free approach: the recorder
// is only used as a clock, and each chunk is decoded and resampled to 16 kHz
// before being posted. Sending PCM is what lets the server split the stream
// cleanly at any moment.
const RATE = 16000;

export async function startLive(
  onText: (text: string, final: boolean) => void,
  language: string,        // required: "bn", "bn-BD", "en-US", …
): Promise<() => void> {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true },
  });

  // Decode the microphone through a context at the target rate, so the samples
  // posted are already 16 kHz mono and no resampling is needed on the server.
  const ctx = new AudioContext({ sampleRate: RATE });
  await ctx.audioWorklet.addModule("/pcm-worklet.js");
  const source = ctx.createMediaStreamSource(stream);
  const worklet = new AudioWorkletNode(ctx, "pcm-worklet");
  source.connect(worklet);

  const controller = new AbortController();
  const upload = new ReadableStream<Uint8Array>({
    start(sink) {
      worklet.port.onmessage = (event: MessageEvent<Float32Array>) => {
        const floats = event.data;
        const pcm = new Int16Array(floats.length);
        for (let i = 0; i < floats.length; i++) {
          const v = Math.max(-1, Math.min(1, floats[i]!));
          pcm[i] = Math.round(v * 32767);
        }
        sink.enqueue(new Uint8Array(pcm.buffer));
      };
    },
    cancel() {
      controller.abort();
    },
  });

  const res = await fetch(`https://zyraxonai.lovable.app/api/public/stt?stream=1&language=${language}`, {
    method: "POST",
    headers: { "Content-Type": "audio/l16; rate=" + RATE, Accept: "text/event-stream" },
    body: upload,
    signal: controller.signal,
    // @ts-expect-error streaming request bodies need this in some browsers
    duplex: "half",
  });

  // Read the event stream and hand each update to the caller.
  const reader = res.body!.getReader();
  const decoder = new TextDecoder();
  let pending = "";
  void (async () => {
    for (;;) {
      const { value, done } = await reader.read();
      if (done) break;
      pending += decoder.decode(value, { stream: true });
      const frames = pending.split("\n\n");
      pending = frames.pop() ?? "";
      for (const frame of frames) {
        const line = frame.split("\n").find((l) => l.startsWith("data: "));
        if (!line) continue;
        const event = JSON.parse(line.slice(6));
        if (event.type === "partial") onText(event.text, false);
        if (event.type === "segment" || event.type === "done") onText(event.text, true);
      }
    }
  })();

  // The caller stops the stream by calling this.
  return () => {
    controller.abort();
    source.disconnect();
    void ctx.close();
    stream.getTracks().forEach((t) => t.stop());
  };
}
Live, in Nodetypescript
// Live transcription from Node: pipe any audio source into the endpoint and
// print text as it is recognised. This is the shape an Electron app or a phone
// recorder uses — it already has a PCM source and wants text as it speaks.
import { spawn } from "node:child_process";
import { Readable } from "node:stream";

const RATE = 16000;
const language = process.env.LANGUAGE;      // required, e.g. bn
if (!language) throw new Error("set LANGUAGE, e.g. LANGUAGE=bn node listen.ts audio.mp3");
const url = `https://zyraxonai.lovable.app/api/public/stt?stream=1&language=${language}`;

// Any ffmpeg-readable input, resampled to the format the endpoint wants.
const ffmpeg = spawn("ffmpeg", [
  "-i", process.argv[2] ?? "-",     // a file, or stdin for a live device
  "-ar", String(RATE),
  "-ac", "1",
  "-f", "s16le",
  "-",
]);

const res = await fetch(url, {
  method: "POST",
  headers: { "Content-Type": `audio/l16; rate=${RATE}`, Accept: "text/event-stream" },
  body: Readable.toWeb(ffmpeg.stdout) as ReadableStream<Uint8Array>,
  // @ts-expect-error Node needs this to stream a request body
  duplex: "half",
});

const reader = res.body!.getReader();
const decoder = new TextDecoder();
let pending = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  pending += decoder.decode(value, { stream: true });
  const frames = pending.split("\n\n");
  pending = frames.pop() ?? "";
  for (const frame of frames) {
    const line = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!line) continue;
    const event = JSON.parse(line.slice(6));
    if (event.type === "partial") process.stdout.write(`\r${event.text}`);
    if (event.type === "segment") console.log(`\n[${event.seconds}s] ${event.text}`);
    if (event.type === "done") console.log(`\ndone: ${event.text}`);
  }
}
Pythonpython
"""Speech to text, batch and live, from Python."""
import json
import requests

STT = "https://zyraxonai.lovable.app/api/public/stt"

# ── a recording you already have ──────────────────────────────────────────
with open("recording.mp3", "rb") as f:
    res = requests.post(STT, files={"file": f}, data={"language": "bn"}, timeout=120)
res.raise_for_status()
answer = res.json()
print(answer["text"], "|", answer.get("language"), answer.get("confidence"))

# ── live: stream PCM and read the events ──────────────────────────────────
def pcm_chunks(path, rate=16000, chunk_bytes=rate * 2):   # one second per chunk
    import subprocess
    proc = subprocess.Popen(
        ["ffmpeg", "-i", path, "-ar", str(rate), "-ac", "1", "-f", "s16le", "-"],
        stdout=subprocess.PIPE,
    )
    while True:
        chunk = proc.stdout.read(chunk_bytes)
        if not chunk:
            break
        yield chunk

with requests.post(
    f"{STT}?language=bn",   # required, e.g. "bn" or "bn-BD"
    data=pcm_chunks("recording.mp3"),
    headers={"Content-Type": "audio/l16; rate=16000", "Accept": "text/event-stream"},
    stream=True,
    timeout=300,
) as res:
    for line in res.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data: "):
            continue
        event = json.loads(line[6:])
        if event["type"] == "partial":
            print("\r" + event["text"], end="", flush=True)
        elif event["type"] == "segment":
            print(f"\n[{event['seconds']}s] {event['text']}")
        elif event["type"] == "done":
            print(f"\ndone: {event['text']}")
The whole loopshell
#!/usr/bin/env bash
# talk.sh — speak a question, hear the answer. The whole loop, both halves.
set -euo pipefail

ORIGIN="https://zyraxonai.lovable.app"
RECORDING="${1:-recording.mp3}"

# 1. Your speech -> text
QUESTION=$(curl -s "$ORIGIN/api/public/stt" -F "file=@$RECORDING" -F "language=bn" | jq -r '.text')
echo "you> $QUESTION"

# 2. Text -> the model's answer
ANSWER=$(curl -s "$ORIGIN/api/public/zyraxon/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg q "$QUESTION" \
        '{model:"zyraxon-apex", messages:[{role:"user", content:$q}]}')" \
  | jq -r '.choices[0].message.content')
echo "zyraxon> $ANSWER"

# 3. The answer -> speech, in the language it was written in
curl -s "$ORIGIN/api/public/tts" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg t "$ANSWER" '{text:$t, locale:"bn"}')" -o reply.mp3
ffplay -nodisp -autoexit reply.mp3    # macOS: afplay   Windows: start
  • Accepted containers: wav, mp3, flac, ogg, webm, and raw PCM. The format is sniffed from the first bytes, not from the Content-Type.
  • Live mode takes raw 16-bit mono PCM. One second at 16 kHz is 32000 bytes — a client paces its microphone by that.
  • A compressed container cannot be cut mid-frame, which is why live mode takes PCM: raw samples can be split at any moment without corrupting the audio.
  • language is required, not detected. Asked for English about Bengali audio the recogniser returns English-sounding words at high confidence instead of failing, so a guess produces a confident wrong answer. The caller knows which language is being spoken; the endpoint asks rather than assumes.
  • GET on this URL returns the contract as JSON, so a coding assistant handed the URL can read it without a human explaining it.
🔊Text to speechSend text, get audioOpen →

If you send us text, we send you audio back. POST the answer and an MP3 streams as it is synthesised, in the language of the text — so a Bengali answer is read by a Bengali voice, not an English one sounding out Bengali letters.

POSThttps://zyraxonai.lovable.app/api/public/tts

Turn an answer into audio. The voice follows the language of the text, so a Bengali answer is read by a Bengali voice rather than an English one sounding out Bengali letters.

Returns audio/mpeg, streamed, with X-ZYRAXON-Voice naming the voice that spoke.

FieldRequiredWhat it does
textyesWhat to say, up to 5000 characters. Markdown is stripped first.
voicenoAn exact name from the voices list, e.g. bn-BD-NabanitaNeural. Wins over locale.
localenoA language, e.g. bn. Picks a voice in that language.
cURLshell
# Speak text. The voice is chosen from the text's own script
# unless you name one, so a Bengali answer is read by a Bengali voice.
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from ZYRAXON.","locale":"en"}' \
  -o answer.mp3

# Name the voice exactly when you want a specific one
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"আজ আকাশ পরিষ্কার।","voice":"bn-BD-NabanitaNeural"}' \
  -o bengali.mp3

# What voices exist: 322 of them, across 75 languages
curl -s https://zyraxonai.lovable.app/api/public/tts/voices | jq -r '.data[] | select(.locale|startswith("bn")) | .name'
1 more example
Browser: speak as it streamstypescript
// Speak an answer, sentence by sentence, while it is still being written.
// Speaking the whole reply at the end means seconds of silence; speaking each
// finished sentence as it appears is what makes it feel like conversation.
const queue: string[] = [];
let buffer = "";
let speaking = false;

async function speak(text: string): Promise<void> {
  const res = await fetch("https://zyraxonai.lovable.app/api/public/tts", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text }),      // the language is read from the text
  });
  const audio = new Audio(URL.createObjectURL(await res.blob()));
  await new Promise<void>((done) => {
    audio.onended = () => done();
    audio.onerror = () => done();
    void audio.play();
  });
}

async function pump(): Promise<void> {
  if (speaking) return;
  speaking = true;
  while (queue.length) await speak(queue.shift()!);
  speaking = false;
}

/** Call on every streamed token of the model's answer. */
export function feed(chunk: string): void {
  buffer += chunk;
  // Cut after a sentence terminator, including the Bengali danda.
  let at = -1;
  for (let i = 0; i < buffer.length; i++) if (/[.!?।॥]/.test(buffer[i]!)) at = i;
  if (at >= 0) {
    queue.push(buffer.slice(0, at + 1).trim());
    buffer = buffer.slice(at + 1).trimStart();
    void pump();
  }
}

/** Call when the answer ends, to speak whatever is left. */
export function finish(): void {
  if (buffer.trim()) queue.push(buffer.trim());
  buffer = "";
  void pump();
}
  • Code blocks, links and Markdown are removed before speaking, so an answer is not read out character by character.
  • Naming voice exactly also skips the voice-catalogue lookup, so the first byte of audio arrives sooner.
Talk to a model2
⚙Tool callingThe model asks, you execute

Define functions in the OpenAI wire shape and the model returns real tool calls you run yourself. The agent loop stays in your program, where your permissions and your side effects live.

FunctionsAgent loop
POST/api/public/zyraxon/v1/chat/completions
◈Capacity and errorsWhat each status means

Overload degrades instead of crashing: a request is shed fast with a 503 and a Retry-After rather than queued behind a stranger. Every status the provider returns, and the retry policy for each.

503Retry-AfterLanes
GET/api/public/zyraxon/v1/provider
Use it anywhere8
▤Termux on AndroidAn agent in your pocket

Run ZYRAXON from a terminal on an Android phone with one setup command. It installs a zyraxon chat box that streams replies and can run a tool-calling agent loop on the device.

AndroidChat boxTool calls
◐Kali Linux and NetHunterSame userland, same client

Kali on a laptop and Kali NetHunter on a phone are the same userland, so the Termux client runs on both. Point it here and the agent has a shell on a security distribution.

KaliNetHunter
✦OpenHandsA custom model that just works

Point OpenHands at this base URL with a ZYRAXON model id and it works — including the two 401s that look alike and the openai/ prefix LiteLLM insists on.

OpenHandsLiteLLMCustom model
⌘Editors and CLI agentsCursor, Cline, Aider, Zed

Any editor or terminal agent that speaks OpenAI works: Cursor, Continue, Cline, Roo Code, Kilo Code, Aider, Zed, and anything shaped like Ollama or LocalAI.

CursorClineAiderZedMCP
▢Mobile appsReact Native, Flutter, native

The same base URL and model id from a phone app. Nothing about the connection is browser-specific, so a React Native, Flutter, Android or iOS client uses the ordinary HTTP stack.

React NativeFlutteriOSAndroid
⟨⟩SDKs and frameworksOne base URL, every client

The OpenAI SDK, the Vercel AI SDK, LangChain, LiteLLM, cURL, and a shell in Python, PowerShell, Bash, Go, Rust, PHP, Java or .NET. Set the base URL and the key, and the framework does the rest.

OpenAI SDKLangChainLiteLLM10 languages
◫Bots, home and automationDiscord, Slack, n8n, Home Assistant

Wire the provider into a chat bot, a home-automation flow or a workflow runner. Same endpoint, same model ids — the bot is just another OpenAI-compatible client.

DiscordSlackn8nHome Assistant
◉Voice in, voice outThe whole loop, end to end

Record on the device, transcribe it, send the transcript as a chat turn, then speak the answer back — a complete hands-free loop across the speech and chat endpoints together.

Speech to textText to speechFull loop

The line

Ten ZYRAXON models

Each model is built for a different job. Capabilities below are read live from the serving pool, so what you see is what is answering right now.

ZYRAXON Apex

Flagship reasoning at a million tokens

Deep reasoning · 1M context · 131K output

zyraxon-apeximagevideoPDF

ZYRAXON Matrix

Sees images, reasons at scale

Vision + text · 1M context · deep reasoning

zyraxon-matriximage

ZYRAXON Pulse

Built for speed on long inputs

Low latency · 262K context · 262K output

zyraxon-pulsetext

ZYRAXON Titan

The long-output workhorse

1M context · 512K output · image + video

zyraxon-titanimagevideo

ZYRAXON Neural

Balanced general intelligence

Reasoning · 200K context · 32K output

zyraxon-neuraltext

ZYRAXON Codex

Writes and repairs code

Code + vision · 200K context

zyraxon-codeximage

ZYRAXON Visionary

Reads images, video and documents

Image + video + PDF · 200K context

zyraxon-visionaryimagevideoPDF

ZYRAXON Cyber

Every input modality, one model

Text + image + audio + video · 200K context

zyraxon-cyberimagevideoaudio

ZYRAXON Quantum

Million-token document reasoning

1M context · vision · 131K output

zyraxon-quantumimagePDF

ZYRAXON Genesis

Fresh from the lab

Latest experimental reasoning · every modality

zyraxon-genesisimagevideoaudioPDF

Automatic rotation

Each ZYRAXON model is bound to a preferred model underneath, with every other model in the pool behind it as a fallback. If one is retired or rate-limited, the same ZYRAXON model keeps answering from the next one — so a Rotated badge means it is covering, not down. You always call the same name; we handle the swap.

Quickstart

Point your client at us

Any OpenAI-compatible client works. Two examples below.

cURLshell
curl https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }],
    "stream": true
  }'
OpenAI SDKtypescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  apiKey: "not-needed",
});

const stream = await client.chat.completions.create({
  model: "zyraxon-codex",
  messages: [{ role: "user", content: "Build a landing page" }],
  stream: true,
});

Response shape

Standard chat-completion chunks. The model field carries the ZYRAXON name you requested, so your logs and billing stay yours.

data: {"id":"zyraxon-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}],"model":"ZYRAXON Apex"}

Multimodal

Send images, audio, video and documents

Chat completions accept the OpenAI content-parts array, so one message can mix text with media. When a turn carries media, it is routed to a model that can actually see it — a video turn is never sent to a text-only model and made to fail.

Vision — image + textjson
{
  "model": "zyraxon-matrix",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "What is in this chart?" },
      { "type": "image_url",
        "image_url": { "url": "data:image/png;base64,..." } }
    ]
  }]
}
Audio, video, documentsjson
{
  "model": "zyraxon-cyber",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Transcribe and summarise" },
      { "type": "input_audio",
        "input_audio": { "data": "<base64>", "format": "wav" } },
      { "type": "video_url",
        "video_url": { "url": "data:video/mp4;base64,..." } },
      { "type": "file",
        "file": { "filename": "report.pdf", "file_data": "data:application/pdf;base64,..." } }
    ]
  }]
}

Send it inline or by URL

Every media part takes either base64 or an https:// URL. Send a URL and the server fetches it for you, so a large file never has to pass through your request body.

Big video just works

A long recording is compressed and sliced in the browser before upload, so a multi-gigabyte file becomes a handful of model-sized parts instead of a failed request.

Capability-aware routing

Every model accepts every modality. Which upstream handles your turn is measured live and chosen server-side, so the right model takes it without you choosing one.

Told what was used

A media response carries X-ZYRAXON-Input-Modalities, so you can tell a real multimodal answer from a text-only one.

The catalogue

Every endpoint, in one list

7 endpoints, none needing a key of any kind. The functions above each link to the ones they use; this is the whole surface for a reader who wants to see it at once.

Every endpoint below is public and needs no key. An agent that would rather read data than a page can fetch the whole catalogue from https://zyraxonai.lovable.app/api/public/zyraxon/v1/endpoints or the plain-text map at /llms.txt.

POSTChat completionsno key
POSThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions

The call your app makes. OpenAI chat-completions semantics, with server-sent-event streaming when you ask for it.

Returns choices[].message.content, or a stream of chat.completion.chunk objects when stream is true.

FieldRequiredWhat it does
modelyesAny id from the model catalogue, e.g. zyraxon-apex.
messagesyesThe usual [{role, content}] array.
streamnotrue for server-sent events.
toolsnoOpenAI function-tool definitions; the model calls them.
Streamingshell
curl -sN https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "stream": true,
    "messages": [{ "role": "user", "content": "Hello" }]
  }'
1 more example
Non-streamingshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }]
  }' | jq -r '.choices[0].message.content'
  • The model value is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
GETModel catalogueno key
GEThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/models

What a client reads first, to discover which models exist. Standard OpenAI list shape, so no special handling is needed.

Returns { object: "list", data: [{ id, owned_by, … }] } — ten ZYRAXON models.

List the lineshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[] | .id'
POSTText to speechno key
POSThttps://zyraxonai.lovable.app/api/public/tts

Turn an answer into audio. The voice follows the language of the text, so a Bengali answer is read by a Bengali voice rather than an English one sounding out Bengali letters.

Returns audio/mpeg, streamed, with X-ZYRAXON-Voice naming the voice that spoke.

FieldRequiredWhat it does
textyesWhat to say, up to 5000 characters. Markdown is stripped first.
voicenoAn exact name from the voices list, e.g. bn-BD-NabanitaNeural. Wins over locale.
localenoA language, e.g. bn. Picks a voice in that language.
cURLshell
# Speak text. The voice is chosen from the text's own script
# unless you name one, so a Bengali answer is read by a Bengali voice.
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from ZYRAXON.","locale":"en"}' \
  -o answer.mp3

# Name the voice exactly when you want a specific one
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"আজ আকাশ পরিষ্কার।","voice":"bn-BD-NabanitaNeural"}' \
  -o bengali.mp3

# What voices exist: 322 of them, across 75 languages
curl -s https://zyraxonai.lovable.app/api/public/tts/voices | jq -r '.data[] | select(.locale|startswith("bn")) | .name'
1 more example
Browser: speak as it streamstypescript
// Speak an answer, sentence by sentence, while it is still being written.
// Speaking the whole reply at the end means seconds of silence; speaking each
// finished sentence as it appears is what makes it feel like conversation.
const queue: string[] = [];
let buffer = "";
let speaking = false;

async function speak(text: string): Promise<void> {
  const res = await fetch("https://zyraxonai.lovable.app/api/public/tts", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text }),      // the language is read from the text
  });
  const audio = new Audio(URL.createObjectURL(await res.blob()));
  await new Promise<void>((done) => {
    audio.onended = () => done();
    audio.onerror = () => done();
    void audio.play();
  });
}

async function pump(): Promise<void> {
  if (speaking) return;
  speaking = true;
  while (queue.length) await speak(queue.shift()!);
  speaking = false;
}

/** Call on every streamed token of the model's answer. */
export function feed(chunk: string): void {
  buffer += chunk;
  // Cut after a sentence terminator, including the Bengali danda.
  let at = -1;
  for (let i = 0; i < buffer.length; i++) if (/[.!?।॥]/.test(buffer[i]!)) at = i;
  if (at >= 0) {
    queue.push(buffer.slice(0, at + 1).trim());
    buffer = buffer.slice(at + 1).trimStart();
    void pump();
  }
}

/** Call when the answer ends, to speak whatever is left. */
export function finish(): void {
  if (buffer.trim()) queue.push(buffer.trim());
  buffer = "";
  void pump();
}
  • Code blocks, links and Markdown are removed before speaking, so an answer is not read out character by character.
  • Naming voice exactly also skips the voice-catalogue lookup, so the first byte of audio arrives sooner.
POSTSpeech to text — one endpoint, two modesno key
POSThttps://zyraxonai.lovable.app/api/public/stt

Send a recording and get the transcript, or stream audio while the person is still speaking and get text as it is recognised. Same URL; the request itself picks the mode.

Returns Batch: { success, text, language, confidence, engine, container, duration, elapsed }. Live: server-sent events — ready, language, partial, segment, done.

FieldRequiredWhat it does
languageyesbn or bn-BD. Required in both modes — the recogniser does not detect language, so a guess would be a wrong transcript.
filenoBatch, multipart: the audio. Format is read from the bytes, so a mislabelled file still works.
audionoBatch, JSON: base64 audio, with an optional format.
segmentnoLive: seconds per committed chunk, default 4.
partialnoLive: seconds between partial updates, default 1.2.
A recordingshell
# A recording you already have. The format is read from the bytes,
# so a mislabelled file still works. language is required — the
# recogniser does not detect it, and a guess is a wrong transcript.
curl -s https://zyraxonai.lovable.app/api/public/stt \
  -F "file=@recording.mp3" \
  -F "language=bn" \
| jq -r '.text'
5 more examples
Live, from a pipeshell
# Live transcription over Server-Sent Events.
# Body: raw 16-bit mono PCM at 16 kHz — one second is 32000 bytes.
# --data-binary with @- streams stdin, so this works from a pipe:
ffmpeg -i recording.mp3 -ar 16000 -ac 1 -f s16le - \
| curl -sN -X POST "https://zyraxonai.lovable.app/api/public/stt?language=bn" \
    -H "Content-Type: audio/l16; rate=16000" \
    -H "Accept: text/event-stream" \
    --data-binary @-

# event: ready     {"rate":16000,"language":"bn"}
# event: partial   {"text":"আজ আকাশ"}
# event: partial   {"text":"আজ আকাশ পরিষ্কার"}
# event: segment   {"text":"আজ আকাশ পরিষ্কার এবং সূর্য উজ্জ্বল।","seconds":4.2}
# event: done      {"text":"...","segments":2,"seconds":8.1}
Live, in a browsertypescript
// Live transcription from a browser microphone.
//
// The browser records to WebM/Opus, which cannot be cut mid-frame, so the audio
// is resampled to raw PCM here with an AudioWorklet-free approach: the recorder
// is only used as a clock, and each chunk is decoded and resampled to 16 kHz
// before being posted. Sending PCM is what lets the server split the stream
// cleanly at any moment.
const RATE = 16000;

export async function startLive(
  onText: (text: string, final: boolean) => void,
  language: string,        // required: "bn", "bn-BD", "en-US", …
): Promise<() => void> {
  const stream = await navigator.mediaDevices.getUserMedia({
    audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true },
  });

  // Decode the microphone through a context at the target rate, so the samples
  // posted are already 16 kHz mono and no resampling is needed on the server.
  const ctx = new AudioContext({ sampleRate: RATE });
  await ctx.audioWorklet.addModule("/pcm-worklet.js");
  const source = ctx.createMediaStreamSource(stream);
  const worklet = new AudioWorkletNode(ctx, "pcm-worklet");
  source.connect(worklet);

  const controller = new AbortController();
  const upload = new ReadableStream<Uint8Array>({
    start(sink) {
      worklet.port.onmessage = (event: MessageEvent<Float32Array>) => {
        const floats = event.data;
        const pcm = new Int16Array(floats.length);
        for (let i = 0; i < floats.length; i++) {
          const v = Math.max(-1, Math.min(1, floats[i]!));
          pcm[i] = Math.round(v * 32767);
        }
        sink.enqueue(new Uint8Array(pcm.buffer));
      };
    },
    cancel() {
      controller.abort();
    },
  });

  const res = await fetch(`https://zyraxonai.lovable.app/api/public/stt?stream=1&language=${language}`, {
    method: "POST",
    headers: { "Content-Type": "audio/l16; rate=" + RATE, Accept: "text/event-stream" },
    body: upload,
    signal: controller.signal,
    // @ts-expect-error streaming request bodies need this in some browsers
    duplex: "half",
  });

  // Read the event stream and hand each update to the caller.
  const reader = res.body!.getReader();
  const decoder = new TextDecoder();
  let pending = "";
  void (async () => {
    for (;;) {
      const { value, done } = await reader.read();
      if (done) break;
      pending += decoder.decode(value, { stream: true });
      const frames = pending.split("\n\n");
      pending = frames.pop() ?? "";
      for (const frame of frames) {
        const line = frame.split("\n").find((l) => l.startsWith("data: "));
        if (!line) continue;
        const event = JSON.parse(line.slice(6));
        if (event.type === "partial") onText(event.text, false);
        if (event.type === "segment" || event.type === "done") onText(event.text, true);
      }
    }
  })();

  // The caller stops the stream by calling this.
  return () => {
    controller.abort();
    source.disconnect();
    void ctx.close();
    stream.getTracks().forEach((t) => t.stop());
  };
}
Live, in Nodetypescript
// Live transcription from Node: pipe any audio source into the endpoint and
// print text as it is recognised. This is the shape an Electron app or a phone
// recorder uses — it already has a PCM source and wants text as it speaks.
import { spawn } from "node:child_process";
import { Readable } from "node:stream";

const RATE = 16000;
const language = process.env.LANGUAGE;      // required, e.g. bn
if (!language) throw new Error("set LANGUAGE, e.g. LANGUAGE=bn node listen.ts audio.mp3");
const url = `https://zyraxonai.lovable.app/api/public/stt?stream=1&language=${language}`;

// Any ffmpeg-readable input, resampled to the format the endpoint wants.
const ffmpeg = spawn("ffmpeg", [
  "-i", process.argv[2] ?? "-",     // a file, or stdin for a live device
  "-ar", String(RATE),
  "-ac", "1",
  "-f", "s16le",
  "-",
]);

const res = await fetch(url, {
  method: "POST",
  headers: { "Content-Type": `audio/l16; rate=${RATE}`, Accept: "text/event-stream" },
  body: Readable.toWeb(ffmpeg.stdout) as ReadableStream<Uint8Array>,
  // @ts-expect-error Node needs this to stream a request body
  duplex: "half",
});

const reader = res.body!.getReader();
const decoder = new TextDecoder();
let pending = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  pending += decoder.decode(value, { stream: true });
  const frames = pending.split("\n\n");
  pending = frames.pop() ?? "";
  for (const frame of frames) {
    const line = frame.split("\n").find((l) => l.startsWith("data: "));
    if (!line) continue;
    const event = JSON.parse(line.slice(6));
    if (event.type === "partial") process.stdout.write(`\r${event.text}`);
    if (event.type === "segment") console.log(`\n[${event.seconds}s] ${event.text}`);
    if (event.type === "done") console.log(`\ndone: ${event.text}`);
  }
}
Pythonpython
"""Speech to text, batch and live, from Python."""
import json
import requests

STT = "https://zyraxonai.lovable.app/api/public/stt"

# ── a recording you already have ──────────────────────────────────────────
with open("recording.mp3", "rb") as f:
    res = requests.post(STT, files={"file": f}, data={"language": "bn"}, timeout=120)
res.raise_for_status()
answer = res.json()
print(answer["text"], "|", answer.get("language"), answer.get("confidence"))

# ── live: stream PCM and read the events ──────────────────────────────────
def pcm_chunks(path, rate=16000, chunk_bytes=rate * 2):   # one second per chunk
    import subprocess
    proc = subprocess.Popen(
        ["ffmpeg", "-i", path, "-ar", str(rate), "-ac", "1", "-f", "s16le", "-"],
        stdout=subprocess.PIPE,
    )
    while True:
        chunk = proc.stdout.read(chunk_bytes)
        if not chunk:
            break
        yield chunk

with requests.post(
    f"{STT}?language=bn",   # required, e.g. "bn" or "bn-BD"
    data=pcm_chunks("recording.mp3"),
    headers={"Content-Type": "audio/l16; rate=16000", "Accept": "text/event-stream"},
    stream=True,
    timeout=300,
) as res:
    for line in res.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data: "):
            continue
        event = json.loads(line[6:])
        if event["type"] == "partial":
            print("\r" + event["text"], end="", flush=True)
        elif event["type"] == "segment":
            print(f"\n[{event['seconds']}s] {event['text']}")
        elif event["type"] == "done":
            print(f"\ndone: {event['text']}")
The whole loopshell
#!/usr/bin/env bash
# talk.sh — speak a question, hear the answer. The whole loop, both halves.
set -euo pipefail

ORIGIN="https://zyraxonai.lovable.app"
RECORDING="${1:-recording.mp3}"

# 1. Your speech -> text
QUESTION=$(curl -s "$ORIGIN/api/public/stt" -F "file=@$RECORDING" -F "language=bn" | jq -r '.text')
echo "you> $QUESTION"

# 2. Text -> the model's answer
ANSWER=$(curl -s "$ORIGIN/api/public/zyraxon/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg q "$QUESTION" \
        '{model:"zyraxon-apex", messages:[{role:"user", content:$q}]}')" \
  | jq -r '.choices[0].message.content')
echo "zyraxon> $ANSWER"

# 3. The answer -> speech, in the language it was written in
curl -s "$ORIGIN/api/public/tts" \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg t "$ANSWER" '{text:$t, locale:"bn"}')" -o reply.mp3
ffplay -nodisp -autoexit reply.mp3    # macOS: afplay   Windows: start
  • Accepted containers: wav, mp3, flac, ogg, webm, and raw PCM. The format is sniffed from the first bytes, not from the Content-Type.
  • Live mode takes raw 16-bit mono PCM. One second at 16 kHz is 32000 bytes — a client paces its microphone by that.
  • A compressed container cannot be cut mid-frame, which is why live mode takes PCM: raw samples can be split at any moment without corrupting the audio.
  • language is required, not detected. Asked for English about Bengali audio the recogniser returns English-sounding words at high confidence instead of failing, so a guess produces a confident wrong answer. The caller knows which language is being spoken; the endpoint asks rather than assumes.
  • GET on this URL returns the contract as JSON, so a coding assistant handed the URL can read it without a human explaining it.

Also available

GETProvider details
/api/public/zyraxon/v1/provider

What the serving pool looks like right now — modalities, concurrency, and each model's live status and context window.

Fields and example
GEThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/provider

What the serving pool looks like right now — modalities, concurrency, and each model's live status and context window.

Returns { models: [{ id, status, context, … }], modalities, concurrency, fleet }.

Read the poolshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/provider | jq '{modalities, concurrency, fleet}'
  • The provider page reads this endpoint live, so a model shown as rotated there is rotated in the pool at that moment.
GETIntegration catalogue
/api/public/zyraxon/v1/integrations

The same integration list this page shows, as JSON — the exact values to paste into a host app, with steps and snippets.

Fields and example
GEThttps://zyraxonai.lovable.app/api/public/zyraxon/v1/integrations

The same integration list this page shows, as JSON — the exact values to paste into a host app, with steps and snippets.

Returns { integrations: [{ id, name, category, fields, steps, snippets }] }.

Find one by idshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/integrations | jq -r '.integrations[] | "\(.id)\t\(.name)"'
GETVoice catalogue
/api/public/tts/voices

Every voice available, with its locale and gender. 322 voices across 75 languages.

Fields and example
GEThttps://zyraxonai.lovable.app/api/public/tts/voices

Every voice available, with its locale and gender. 322 voices across 75 languages.

Returns { count, languages, locales, data: [{ name, locale, gender, friendly }] }.

Every Bengali voiceshell
curl -s https://zyraxonai.lovable.app/api/public/tts/voices \
  | jq -r '.data[] | select(.locale | startswith("bn")) | .name'
# bn-BD-NabanitaNeural
# bn-BD-PradeepNeural
# bn-IN-BashkarNeural
# bn-IN-TanishaaNeural

Anywhere

Use it from anything

Termux on a phone, OpenHands, an editor, a mobile app, an SDK, a workflow runner — they all connect the same way. Base URL https://zyraxonai.lovable.app/api/public/zyraxon/v1, model zyraxon-apex, any non-empty API key.

Base URL

https://zyraxonai.lovable.app/api/public/zyraxon/v1

Default model

zyraxon-apex

API key

zyraxon
Terminal & Mobile3

Termux (Android)

Run ZYRAXON from the terminal on an Android phone. The setup script installs curl and jq, stores your settings, and adds a zyraxon command that opens an interactive chat box — type a message, get a streamed reply, switch models, and turn on tool calling so the agent can read, write and run things on the phone.

AndroidChat boxAny modelTool calls
How to connect

Steps

  1. Install Termux from F-Droid (the Play Store build is abandoned).
  2. Run the setup script: curl -fsSL https://zyraxonai.lovable.app/integrations/termux-setup.sh | bash
  3. Open a new shell, or run source ~/.bashrc, so zyraxon is on your PATH.
  4. Run zyraxon with no arguments to open the chat box, then just type.
  5. Use /models and /model to pick any model the provider serves, and /agent on to let it read, write and run commands on the device.

Code

The chat boxtext
$ zyraxon

  ZYRAXON  ·  chat
  model zyraxon-codex   ·   tools off
  /help for commands, /exit to leave

you> explain what this repo does in three bullets
zyraxon> 1. It is a marketing and tooling site for the ZYRAXON desktop agent.
         2. Provider routes live under src/routes/api/public/zyraxon/v1.
         3. The docs are data files under src/lib/docs.

you> /agent on
tools on
you> make hello.py that prints hi, then run it
  -> write_file
write hello.py (24 bytes)? [y/N] y
  -> run_shell
run: python3 hello.py ? [y/N] y
Done — it printed: hi

you> /model 2
model is now zyraxon-apex

you> /exit
bye
Commandstext
Commands inside the chat box
  /help            show this list
  /models          list every model the provider serves
  /model <n|id>    switch model — by number from /models, or by id
  /agent [on|off]  toggle tool calling (read, write, run) for the session
  /new             forget the conversation and start fresh
  /history         how many messages are in the conversation
  /save <file>     write the conversation to a file as JSON
  /exit            leave (also /quit)

Anything else you type is sent to the model.

One-shot, from the shell
  zyraxon "explain this repo"            stream one answer and exit
  zyraxon -a "make hello.py and run it"  one agent turn with tools, then exit
  zyraxon -m zyraxon-apex "..."          pick a model for one turn
  echo "what is 2+2?" | zyraxon          read the prompt from stdin
  zyraxon --models                       list the models

Any model works. The model list is read live, so every id the provider serves
today is selectable with /model — there is no fixed list in the client.
Setupbash
# One command. Installs deps, writes ~/.zyraxon/env, installs the client.
curl -fsSL https://zyraxonai.lovable.app/integrations/termux-setup.sh | bash

# Then, from any directory:
zyraxon                                  # open the chat box
zyraxon "summarise the changes in this repo"
zyraxon -a "create a hello world python file and run it"
echo "what is 2+2?" | zyraxon
Manual setupbash
pkg update && pkg upgrade -y
pkg install -y curl jq python

export ZYRAXON_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export ZYRAXON_MODEL="zyraxon-apex"
export ZYRAXON_KEY="zyraxon"

# The client on its own, without the installer:
curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon-chat.sh -o ~/.local/bin/zyraxon
chmod +x ~/.local/bin/zyraxon

curl -s "${ZYRAXON_BASE}/models" | jq -r '.data[].id'
Streaming one-linerbash
curl -sN "${ZYRAXON_BASE}/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${ZYRAXON_KEY}" \
  -d "{\"model\":\"${ZYRAXON_MODEL}\",\"stream\":true,
       \"messages\":[{\"role\":\"user\",\"content\":\"$1\"}]}" \
| sed -n 's/^data: //p' \
| grep -v '^\[DONE\]$' \
| jq -rj '.choices[0].delta.content // empty'
echo
  • •The chat box keeps the conversation, so follow-up questions work. /new clears it.
  • •Every model is selectable: the client reads the model list live, so there is no hardcoded list to go stale.
  • •Termux has no GPU and no browser, so keep media on the phone small. Images under 2 MB inline work; video should be compressed before sending.
  • •In agent mode the client runs three tools — read, write, run — and asks before it writes or executes anything. It is deliberately not a general shell escape hatch.
  • •Android may kill Termux in the background. Run termux-wake-lock before a long agent turn.
Download termux-setup.sh

Kali Linux (desktop and NetHunter)

Kali is a Debian-family distro with a shell, so the provider works exactly as it does on any Linux box — and Kali NetHunter puts the same client on an Android phone inside its chroot. One zyraxon-ask script gives you a streaming prompt with your environment already wired.

KaliNetHunterDebianShell
How to connect

Steps

  1. Install the two tools the client needs: sudo apt install -y curl jq.
  2. Export ZYRAXON_BASE, ZYRAXON_MODEL and ZYRAXON_KEY, or paste them into ~/.bashrc.
  3. Download kali-ask.sh, chmod +x it, and put it on your PATH.
  4. Ask something: zyraxon-ask "explain the output of this nmap scan".
  5. On NetHunter, start the chroot with nethunter first, then do the same inside it.

Code

Setupbash
# Kali Linux / any Debian-family distro, including Kali NetHunter on a phone.
sudo apt update && sudo apt install -y curl jq

export ZYRAXON_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export ZYRAXON_MODEL="zyraxon-apex"
export ZYRAXON_KEY="zyraxon"

# Keep it for new shells.
cat >> ~/.bashrc <<'EOF'
export ZYRAXON_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export ZYRAXON_MODEL="zyraxon-apex"
export ZYRAXON_KEY="zyraxon"
EOF

curl -s "${ZYRAXON_BASE}/models" | jq -r '.data[].id'
zyraxon-ask scriptbash
#!/usr/bin/env bash
# zyraxon-ask — stream one turn from the ZYRAXON line on Kali / any Linux shell.
# Usage: zyraxon-ask "why does nmap show port 22 open?"
set -euo pipefail
: "${ZYRAXON_BASE:?set ZYRAXON_BASE first}"
: "${ZYRAXON_KEY:?set ZYRAXON_KEY first}"
PROMPT="${*:-$(cat)}"

curl -sN "${ZYRAXON_BASE}/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${ZYRAXON_KEY}" \
  -d "$(jq -nc --arg m "${ZYRAXON_MODEL}" --arg p "${PROMPT}" \
        '{model:$m, stream:true, messages:[{role:"user",content:$p}]}')" \
| sed -n 's/^data: //p' \
| grep -v '^\[DONE\]$' \
| jq -rj '.choices[0].delta.content // empty'
echo
Kali NetHunter (chroot)bash
# Kali NetHunter: run the same client inside the Kali chroot on an Android phone.
# (Termux side: install the NetHunter app, then start the chroot.)
nethunter
apt update && apt install -y curl jq

export ZYRAXON_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export ZYRAXON_MODEL="zyraxon-apex"
export ZYRAXON_KEY="zyraxon"

curl -s "${ZYRAXON_BASE}/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${ZYRAXON_KEY}" \
  -d "{\"model\":\"${ZYRAXON_MODEL}\",\"messages\":[{\"role\":\"user\",\"content\":\"list my open ports\"}]}" \
| jq -r '.choices[0].message.content'
Pipe tool outputbash
# Give the model real evidence instead of a description of it.
nmap -sV -T4 192.168.1.1 -oN - | zyraxon-ask "summarise these findings for a report"
ss -tulpn | zyraxon-ask "which of these services is most exposed, and why?"
journalctl -p err -n 50 | zyraxon-ask "what broke here?"
  • •NetHunter's chroot is a full Kali userland, so anything that works on a Kali desktop works there — the phone is the only difference.
  • •Keep API keys out of shared shell history: put them in ~/.zyraxon/env with mode 600 and source it, rather than typing them inline.
Download kali-ask.sh

Mobile apps (React Native, Flutter, Android, iOS)

A mobile app does not need an OpenAI SDK. The endpoint is plain HTTPS with CORS open to *, so the platform HTTP client is enough — and the media parts are the same shapes the web client already sends.

React NativeFlutterKotlinSwift
How to connect

Code

React Native / Expotypescript
// React Native / Expo / any mobile JS runtime. No SDK needed.
const res = await fetch("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions", {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: "Bearer zyraxon" },
  body: JSON.stringify({
    model: "zyraxon-apex",
    messages: [{ role: "user", content: "Plan my day." }],
    stream: false,
  }),
});

const data = await res.json();
console.log(data.choices[0].message.content);
Flutter / Dartdart
import 'dart:convert';
import 'package:http/http.dart' as http;

final res = await http.post(
  Uri.parse('https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions'),
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer zyraxon',
  },
  body: jsonEncode({
    'model': 'zyraxon-apex',
    'messages': [
      {'role': 'user', 'content': 'Plan my day.'},
    ],
  }),
);

final content = jsonDecode(res.body)['choices'][0]['message']['content'];
Android / Kotlinkotlin
// Plain HTTP, so it works from an Android app with no OpenAI SDK at all.
val client = OkHttpClient()
val json = """
  {"model":"zyraxon-apex","messages":[{"role":"user","content":"Plan my day."}]}
""".trimIndent()

val request = Request.Builder()
    .url("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions")
    .addHeader("Content-Type", "application/json")
    .addHeader("Authorization", "Bearer zyraxon")
    .post(json.toRequestBody("application/json".toMediaType()))
    .build()

client.newCall(request).execute().use { response ->
    println(response.body?.string())
}
iOS / Swiftswift
// URLSession, no SDK. Works on iOS, iPadOS and watchOS.
var request = URLRequest(url: URL(string: "https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer zyraxon", forHTTPHeaderField: "Authorization")
request.httpBody = try JSONSerialization.data(withJSONObject: [
    "model": "zyraxon-apex",
    "messages": [["role": "user", "content": "Plan my day."]],
])

let (data, _) = try await URLSession.shared.data(for: request)
print(String(data: data, encoding: .utf8) ?? "")
  • •CORS allows any origin, so a WebView-based app can call the endpoint directly.
  • •For voice on mobile, POST the recording to /api/public/stt and send the transcript as a normal text turn.
  • •Keep large media off the device path: compress on the phone first, then send one or two parts.
Agents & Editors6

OpenHands

OpenHands takes a custom provider through a small form. Copy the four fields below and it uses the ZYRAXON line like any other OpenAI-compatible model.

Custom providerBase URL + API key
How to connect

Connection fields

Name (Optional)
zyraxon
Custom Model
openai/zyraxon-apex

Copy this exactly, openai/ included. OpenHands routes the model through LiteLLM, which refuses an id with no provider prefix — that is the "LLM Provider NOT provided" error.

Base URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1

Copy this exactly, including /v1 at the end.

API Key
zyraxon

Any non-empty value, unless the operator set ZYRAXON_PROVIDER_KEY.

Steps

  1. Open OpenHands and go to Settings, then the LLM (model / provider) section.
  2. Turn Advanced on at the top of the page. This matters: the Base URL field only takes effect in Advanced mode. In the simple form OpenHands ignores it and sends the request to OpenAI instead — which is what produces 401 Incorrect API key provided: zyraxon.
  3. Choose Custom Model.
  4. For Custom Model, copy openai/zyraxon-apex exactly. The openai/ prefix is what LiteLLM reads to pick a handler; without it OpenHands fails before it calls us, with LLM Provider NOT provided.
  5. For Base URL, copy https://zyraxonai.lovable.app/api/public/zyraxon/v1 exactly. If this box is missing, Advanced is not on yet.
  6. For API Key, use any non-empty value — zyraxon here.
  7. Press Save Changes, then start a conversation.

Code

Copy these fourtext
Name (Optional)   zyraxon
Custom Model      openai/zyraxon-apex
Base URL          https://zyraxonai.lovable.app/api/public/zyraxon/v1
API Key           zyraxon
Turn Advanced ON firsttext
OpenHands has two modes, and the Base URL only works in one of them.

  Simple form:  there is a "Provider connection" dropdown, no Base URL box
                -> OpenHands sends the turn to OpenAI itself
                -> "401 Incorrect API key provided: zyraxon"

  Advanced:     a Base URL box appears
                -> OpenHands sends the turn to whatever that box says

Turn "Advanced" on at the top of the LLM settings page before you fill
anything in. The Base URL box appears once it is on.
The "401 Incorrect API key" errortext
  Error code: 401 - Incorrect API key provided: zyraxon.
  You can find your API key at https://platform.openai.com/account/api-keys

That message comes from OpenAI, not from us — the request never reached the
ZYRAXON endpoint. It means OpenHands used its default OpenAI URL instead of
your Base URL, almost always because Advanced mode is off.

Fix: turn Advanced on, then set Base URL to
  https://zyraxonai.lovable.app/api/public/zyraxon/v1

You can confirm it reaches us, and not OpenAI, in one call:

  curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[].id'
  # prints the ZYRAXON model ids -> the URL is right
The "LLM Provider NOT provided" errortext
If OpenHands reports:

  litellm.BadRequestError: LLM Provider NOT provided.
  You passed model=zyraxon-apex

the Custom Model field is missing its provider prefix. OpenHands routes the
model through LiteLLM, which reads the text before the first "/" to choose a
handler and refuses a bare id — it never reaches us. Add the prefix:

  Custom Model:  openai/zyraxon-apex

The endpoint accepts both spellings, so this works with no other change:

  zyraxon-apex   openai/zyraxon-apex   zyraxon/zyraxon-apex   zyraxon/codex

If your version reads the model from the environment instead of the form:

  export LLM_MODEL="openai/zyraxon-apex"
  export LLM_BASE_URL="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
  export LLM_API_KEY="zyraxon"
Verify before you savebash
# The model list is what the form needs to resolve the model id.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[].id'

# A one-shot turn proves the URL and key are right. Both spellings work here.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer zyraxon" \
  -d '{"model":"openai/zyraxon-apex","messages":[{"role":"user","content":"ping"}]}' \
| jq -r '.choices[0].message.content'
  • •Turn Advanced on. Without it the Base URL is ignored and OpenHands calls OpenAI, which answers 401 Incorrect API key provided.
  • •OpenHands needs the model id to exist in the list the base URL serves. GET /models publishes all ten, so the form resolves them without a manual override.
  • •Streaming is on by default in OpenHands; the endpoint serves SSE when stream: true.
  • •If you see LLM Provider NOT provided, put openai/ in front of the model: openai/zyraxon-apex. OpenHands routed it through LiteLLM, which needs a provider prefix. The endpoint accepts either spelling, so nothing else has to change.

Cursor, Continue, Cline, Aider and any OpenAI-compatible app

Anything with an *OpenAI-compatible* or *custom base URL* setting connects with the same two values. This is the largest group of clients, and the one that needs no code at all.

Drop-inNo code
How to connect

Connection fields

Base URL / API base
https://zyraxonai.lovable.app/api/public/zyraxon/v1
Model
zyraxon-apex
API key
zyraxon

Steps

  1. Find the provider or model settings in the app.
  2. Choose the OpenAI-compatible / custom option.
  3. Paste https://zyraxonai.lovable.app/api/public/zyraxon/v1 as the base URL and zyraxon-apex as the model.
  4. Put zyraxon in the key field if the app insists on one.
  5. Reload the app so it re-fetches the model list.

Code

Continue (config.json)json
{
  "models": [
    {
      "title": "ZYRAXON",
      "provider": "openai",
      "model": "zyraxon-apex",
      "apiBase": "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
      "apiKey": "zyraxon"
    }
  ]
}
Aiderbash
export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"
aider --model openai/zyraxon-apex
Generic environment variablesbash
# Many CLIs read these directly.
export OPENAI_BASE_URL="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"
  • •Some editors cache the model list at install time. Reload after changing the base URL, or the old list keeps being offered.
  • •If an editor only accepts a fixed set of model names, pick the closest ZYRAXON id — the model id is a name, not a capability gate.

VS Code: Cline, Roo Code, Kilo Code and Copilot Chat

The VS Code agent extensions all expose an *OpenAI Compatible* provider, so they connect with the same base URL and key as everything else. Copilot Chat and extensions that read the OpenAI environment variables work too.

VS CodeClineRoo CodeCopilot
How to connect

Connection fields

Base URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1
API key
zyraxon
Model ID
zyraxon-apex

Steps

  1. Open the extension's settings and choose the OpenAI Compatible provider.
  2. Paste https://zyraxonai.lovable.app/api/public/zyraxon/v1 as the base URL and zyraxon-apex as the model id.
  3. Put zyraxon in the key field if the extension insists on one.
  4. Reload the window so the extension re-reads the model list.

Code

Cline / Roo (settings.json)json
// Cline / Roo Code / Kilo Code (VS Code settings.json)
// These extensions expose an "OpenAI Compatible" provider.
{
  "cline.apiProvider": "openai",
  "cline.openAiBaseUrl": "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  "cline.openAiApiKey": "zyraxon",
  "cline.openAiModelId": "zyraxon-apex"
}
Roo Codetext
// Roo Code — Settings → Providers → OpenAI Compatible
//   Base URL:  https://zyraxonai.lovable.app/api/public/zyraxon/v1
//   API Key:   zyraxon
//   Model ID:  zyraxon-apex
// Or, from the CLI, by environment:
export OPENAI_BASE_URL="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"
Copilot Chat / envbash
// VS Code + GitHub Copilot Chat, "Bring your own model" style providers,
// or any extension that reads the OpenAI base URL from the environment.
export OPENAI_BASE_URL="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"
# Then start VS Code from that shell so it inherits the variables.
code .
  • •Extensions that cache the model list at install time need a window reload after the base URL changes.

Zed

Zed takes a custom OpenAI-compatible URL in its settings, so the assistant and the inline panel both run on the ZYRAXON line.

ZedEditor
How to connect

Code

settings.jsonjson
// Zed — settings.json
{
  "language_models": {
    "openai": {
      "api_url": "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
      "available_models": [
        { "name": "zyraxon-apex", "max_tokens": 16000 }
      ]
    }
  }
}

Ollama-style and LocalAI-style clients

Clients that expect a local model server on localhost can be pointed at us instead, because they speak the same OpenAI-compatible dialect.

Drop-inLocal server replacement
How to connect

Code

Environmentbash
# Any client that speaks "Ollama" or "LocalAI" over an OpenAI-compatible
# /v1 base works too — point it at us instead of localhost.
export OLLAMA_HOST="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_BASE_URL="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"

# Verify the same way any OpenAI client would:
curl -s "https://zyraxonai.lovable.app/api/public/zyraxon/v1/models" | jq -r '.data[].id'

MCP servers and agent tool hosts

An MCP server or any agent framework can use ZYRAXON as its model and keep its own tool definitions — they travel in the request and come back as real tool calls.

MCPTool calls
How to connect

Code

Tool-carrying requestjson
// An MCP server, or any agent tool host, can call ZYRAXON as its model.
// The tool definitions travel in the request; the model returns real calls.
{
  "model": "zyraxon-apex",
  "messages": [{ "role": "user", "content": "Read package.json and summarise it." }],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "read_file",
        "description": "Read a file from the working directory.",
        "parameters": {
          "type": "object",
          "properties": { "path": { "type": "string" } },
          "required": ["path"]
        }
      }
    }
  ]
}
SDKs & Frameworks8

cURL

The shortest path to a working call. Two headers and a JSON body, and it works from any machine with curl — including a phone.

AnywhereNo install
How to connect

Code

Streaming chatshell
curl https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }],
    "stream": true
  }'
Non-streaming, read the answershell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zyraxon-apex",
    "messages": [{ "role": "user", "content": "Hello" }]
  }' | jq -r '.choices[0].message.content'
Discover the lineshell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models      | jq -r '.data[] | "\(.id)  \(.context_window)"'
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/provider    | jq '{modalities, concurrency, fleet}'

OpenAI SDK (Node and Python)

The official SDKs work unchanged — only the base URL moves. This is the path to use when you want retries, typed responses and streaming without writing HTTP by hand.

TypeScriptPythonStreaming
How to connect

Code

Node / TypeScripttypescript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  apiKey: "zyraxon",
});

const stream = await client.chat.completions.create({
  model: "zyraxon-apex",
  messages: [{ role: "user", content: "Build a landing page" }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
Pythonpython
from openai import OpenAI

client = OpenAI(base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1", api_key="zyraxon")

stream = client.chat.completions.create(
    model="zyraxon-apex",
    messages=[{"role": "user", "content": "Build a landing page"}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)
Streaming (Python)python
from openai import OpenAI

client = OpenAI(base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1", api_key="zyraxon")

stream = client.chat.completions.create(
    model="zyraxon-apex",
    messages=[{"role": "user", "content": "Write a haiku about the sea."}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)
Tool calling loop (TypeScript)typescript
import OpenAI from "openai";

const client = new OpenAI({ baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1", apiKey: "zyraxon" });

const tools = [
  {
    type: "function" as const,
    function: {
      name: "write_file",
      description: "Write text to a file on disk.",
      parameters: {
        type: "object",
        properties: { path: { type: "string" }, content: { type: "string" } },
        required: ["path", "content"],
      },
    },
  },
];

// You run the tools. The provider only returns the call.
async function runTool(name: string, args: any) {
  if (name === "write_file") {
    const { writeFile } = await import("node:fs/promises");
    await writeFile(args.path, args.content, "utf8");
    return `wrote ${args.path}`;
  }
  return `unknown tool ${name}`;
}

const messages: any[] = [{ role: "user", content: "Save a hello world file." }];

for (let turn = 0; turn < 8; turn++) {
  const res = await client.chat.completions.create({
    model: "zyraxon-apex",
    messages,
    tools,
  });
  const choice = res.choices[0];
  messages.push(choice.message);

  if (choice.finish_reason !== "tool_calls") break;

  for (const call of choice.message.tool_calls ?? []) {
    const result = await runTool(call.function.name, JSON.parse(call.function.arguments));
    messages.push({ role: "tool", tool_call_id: call.id, content: result });
  }
}
Tool calling loop (Python)python
from openai import OpenAI
import json, pathlib

client = OpenAI(base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1", api_key="zyraxon")

tools = [{
    "type": "function",
    "function": {
        "name": "write_file",
        "description": "Write text to a file on disk.",
        "parameters": {
            "type": "object",
            "properties": {"path": {"type": "string"}, "content": {"type": "string"}},
            "required": ["path", "content"],
        },
    },
}]

def run_tool(name, args):
    if name == "write_file":
        pathlib.Path(args["path"]).write_text(args["content"], encoding="utf-8")
        return f"wrote {args['path']}"
    return f"unknown tool {name}"

messages = [{"role": "user", "content": "Save a hello world file."}]

for _ in range(8):
    res = client.chat.completions.create(model="zyraxon-apex", messages=messages, tools=tools)
    choice = res.choices[0]
    messages.append(choice.message)

    if choice.finish_reason != "tool_calls":
        break

    for call in choice.message.tool_calls or []:
        result = run_tool(call.function.name, json.loads(call.function.arguments))
        messages.append({"role": "tool", "tool_call_id": call.id, "content": result})
  • •function.arguments is a JSON string, not an object. Parse it before use, and accumulate streamed fragments by index.

Vercel AI SDK

Use the OpenAI-compatible provider from @ai-sdk/openai-compatible. This is the same transport the ZYRAXON builder gateway uses, so the shapes are already proven.

TypeScriptStreaming
How to connect

Code

TypeScripttypescript
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { streamText } from "ai";

const zyraxon = createOpenAICompatible({
  name: "zyraxon",
  baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
  apiKey: "zyraxon",
});

const result = streamText({
  model: zyraxon("zyraxon-apex"),
  messages: [{ role: "user", content: "Explain a monad in one paragraph." }],
});

for await (const delta of result.textStream) process.stdout.write(delta);

LangChain

LangChain's OpenAI integrations accept a custom base URL, so the whole chain, agent and tooling surface is available over the ZYRAXON line.

PythonJavaScript
How to connect

Code

Pythonpython
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="zyraxon-apex",
    base_url="https://zyraxonai.lovable.app/api/public/zyraxon/v1",
    api_key="zyraxon",
)

print(llm.invoke("Summarise this repo layout in three bullets.").content)
JavaScripttypescript
import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "zyraxon-apex",
  configuration: { baseURL: "https://zyraxonai.lovable.app/api/public/zyraxon/v1", apiKey: "zyraxon" },
});

console.log((await llm.invoke("Summarise this repo layout in three bullets.")).content);

LiteLLM (and Open WebUI, LibreChat, Dify, Flowise)

LiteLLM refuses a model string without a provider prefix — that is the "LLM Provider NOT provided" error. Write the model as openai/zyraxon-codex and it uses the generic OpenAI-compatible handler. Every LiteLLM-based gateway (Open WebUI, LibreChat, Dify, Flowise, Langroid) connects the same way.

PythonProxyOpen WebUIGateways
How to connect

Connection fields

Model
openai/zyraxon-apex

The openai/ prefix is required by LiteLLM.

API base / api_base
https://zyraxonai.lovable.app/api/public/zyraxon/v1
API key
zyraxon

Steps

  1. Use the model string with its provider prefix: openai/zyraxon-codex. Without a prefix LiteLLM stops before it ever calls us.
  2. Set the API base to https://zyraxonai.lovable.app/api/public/zyraxon/v1 and the key to any non-empty value.
  3. For a gateway like Open WebUI, add a custom OpenAI connection with those same values and reload.
  4. To front the whole line with LiteLLM's own proxy, use the config file below.

Code

Pythonpython
from litellm import completion

response = completion(
    # The provider prefix is required: LiteLLM reads it to pick a handler, and
    # without it you get "LLM Provider NOT provided". The endpoint strips it.
    model="openai/zyraxon-apex",
    api_base="https://zyraxonai.lovable.app/api/public/zyraxon/v1",
    api_key="zyraxon",
    messages=[{"role": "user", "content": "Explain what a reverse proxy does."}],
)

print(response.choices[0].message.content)
litellm-config.yamlyaml
# litellm-config.yaml — a LiteLLM proxy that fronts ZYRAXON.
#
# Start it with:  litellm --config litellm-config.yaml --port 4000
# Then any client pointed at http://localhost:4000/v1 gets the ZYRAXON models,
# with LiteLLM's own routing, budgets, logging and key management on top.
model_list:
  - model_name: zyraxon-apex
    litellm_params:
      model: openai/zyraxon-apex
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon
  - model_name: zyraxon-codex
    litellm_params:
      model: openai/zyraxon-codex
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon
  - model_name: zyraxon-cyber
    litellm_params:
      model: openai/zyraxon-cyber
      api_base: https://zyraxonai.lovable.app/api/public/zyraxon/v1
      api_key: zyraxon

litellm_settings:
  drop_params: true
Environmentbash
# Point LiteLLM at ZYRAXON with plain environment variables, no config file.
# The openai/ prefix in the model string is required by LiteLLM.
export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"

python - <<'PY'
from litellm import completion
r = completion(model="openai/zyraxon-apex",
               messages=[{"role": "user", "content": "hello"}])
print(r.choices[0].message.content)
PY

# Same thing through the LiteLLM CLI:
litellm --model openai/zyraxon-apex --api_base https://zyraxonai.lovable.app/api/public/zyraxon/v1 --api_key zyraxon
Model id rulestext
# Why the model string needs a provider prefix, and what this endpoint accepts.
#
# LiteLLM reads the text before the first "/" to choose a handler. A bare id
# like "zyraxon-codex" has no "/", so LiteLLM stops with:
#     BadRequestError: LLM Provider NOT provided
# Write "openai/zyraxon-codex" and it uses the generic OpenAI-compatible path.
#
# This endpoint is tolerant: all of these resolve to the same ZYRAXON model.
#   zyraxon-codex
#   openai/zyraxon-codex
#   zyraxon/zyraxon-codex
#   zyraxon/codex
#   openai/zyraxon/codex
  • •The endpoint is tolerant of the prefix: zyraxon-codex, openai/zyraxon-codex, zyraxon/zyraxon-codex and zyraxon/codex all resolve to the same model.
  • •drop_params: true in the proxy config avoids LiteLLM sending parameters an OpenAI-compatible endpoint may not accept.
  • •This is the integration to reach for when a client insists on a provider/model string.

Python shell, REPL and notebooks

Import one dependency-free file and the provider is a function call. Works in the plain REPL, IPython, Jupyter, Colab, a script or a cron job. Every model works — the list is read live, so nothing is pinned.

PythonREPLJupyterTool calls
How to connect

Connection fields

Base URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1
Model
zyraxon-apex

Any model id; ai.models() lists them live.

API key
zyraxon

Steps

  1. Download the client: curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.py -o zyraxon.py.
  2. In a shell, import it: from zyraxon import Zyraxon.
  3. Call ai.ask(...) for a whole answer, or loop over ai.stream(...) to print as it arrives.
  4. Ask a follow-up on the same ai and it remembers the conversation; call ai.reset() to start fresh.
  5. Use ai.models() to see every model the provider serves right now, then Zyraxon(model=...) to pick one.
  6. For tool calling, pass ai.agent(prompt, tools, run) your own tools — the loop is handled for you.

Code

Python shellpython
# Fetch it
curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.py -o zyraxon.py

# In any Python shell, REPL or notebook
>>> from zyraxon import Zyraxon
>>> ai = Zyraxon()                      # any model; models() lists them live
>>> ai.ask("explain what a race condition is")
>>> for piece in ai.stream("write a haiku about the sea"):
...     print(piece, end="")
>>> ai.ask("and in Go?")                # the conversation is remembered
>>> Zyraxon(model="zyraxon-apex").ask("reason about this carefully")

# As a command, for scripts and pipes
python zyraxon.py "summarise this repo"
python zyraxon.py -a "create hello.py and run it"     # tool-calling loop
python zyraxon.py --models
echo "what is 2+2?" | python zyraxon.py
Work that runs for hourspython
# A job that runs for hours, and is never lost.
#
# A single HTTP request cannot live for hours — a dropped network, a restart or
# a deploy ends it. So the run is kept in ~/.zyraxon/sessions/<name>.json,
# rewritten after every step, and resumes from there after any interruption.
# Stop it, kill it, reboot the machine: start it again with the same session
# name and it continues from the last step instead of starting over.

from zyraxon import Zyraxon, local_tools

ai = Zyraxon(model="zyraxon-apex")
tools, run = local_tools(confirm=False)

result = ai.long_task(
    "Rewrite every file under src/lib, one file per step, keeping the tests green.",
    tools,
    run,
    session="refactor",          # same name resumes this run
    hours=0,                     # 0 = keep going until the task is done
    on_step=lambda s: print(f"step {s['step']}"),   # progress as it goes
)
print(result)

# From a shell, the same thing:
#   python zyraxon.py --long --session refactor "rewrite each file in src"
#   python zyraxon.py --sessions          # see saved runs and their step
A tool-calling agentpython
from zyraxon import Zyraxon, local_tools

ai = Zyraxon()
tools, run = local_tools(confirm=True)   # read_file, write_file, run_shell
print(ai.agent("create hello.py that prints hi, then run it", tools, run))
Streaming into your own codepython
from zyraxon import Zyraxon

ai = Zyraxon(model="zyraxon-pulse")     # the fast one
for piece in ai.stream("summarise the changes in src/lib"):
    print(piece, end="", flush=True)
  • •No dependencies: it uses only the standard library, so it runs anywhere Python does — including a locked-down machine where pip install is not allowed.
  • •ask() keeps the conversation, so a follow-up has the context. reset() clears it.
  • •Any model works. models() reads the list live, so a model added later is usable with no code change.
  • •Work that runs for hours: ai.long_task(...) keeps the run in ~/.zyraxon/sessions and resumes from it after any interruption — a dropped network, a killed shell, a reboot. Start it, walk away, and it carries on from the last step instead of starting over.
Download zyraxon.py

PowerShell

Dot-source one script and the provider becomes four functions. Works in Windows PowerShell and PowerShell 7 on every platform, with functions for a plain answer, a streamed answer, and the tool-calling agent loop.

PowerShellWindowsTool calls
How to connect

Connection fields

Base URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1
Model
zyraxon-apex

Any model id; Get-ZyraxonModels lists them live.

API key
zyraxon

Steps

  1. Download it: Invoke-WebRequest https://zyraxonai.lovable.app/integrations/zyraxon.ps1 -OutFile zyraxon.ps1.
  2. Dot-source it in your session: . .\zyraxon.ps1.
  3. Call Invoke-Zyraxon "..." for an answer, or add -Stream to watch it arrive.
  4. Use -Model <id> for one turn, or Set-ZyraxonConfig -Model <id> for the session.
  5. For tool calling, call Invoke-ZyraxonAgent "..." -Confirm so it asks before writing or running anything.
  6. Get-ZyraxonModels lists every model the provider serves right now.

Code

PowerShellpowershell
# Fetch it
Invoke-WebRequest https://zyraxonai.lovable.app/integrations/zyraxon.ps1 -OutFile zyraxon.ps1

# Dot-source it, then use the functions
. .\zyraxon.ps1

Get-ZyraxonModels                                   # any model, live
Invoke-Zyraxon "explain what a race condition is"
Invoke-Zyraxon "write a haiku about the sea" -Stream
Invoke-Zyraxon "reason about this" -Model zyraxon-apex
Invoke-ZyraxonAgent "create hello.py that prints hi, then run it" -Confirm

# Or a one-shot run
.\zyraxon.ps1 "summarise this repo" -Stream

# Point it somewhere else
Set-ZyraxonConfig -Model zyraxon-pulse
Pipe a file inpowershell
Get-Content .\error.log -Raw | ForEach-Object {
    Invoke-Zyraxon "what went wrong in this log?"
}
  • •No modules required — it uses Invoke-RestMethod, which ships with PowerShell.
  • •Invoke-ZyraxonAgent -Confirm asks before any write or command; without it, it runs unattended.
  • •Get-ZyraxonModels reads the list live, so any model the provider serves works with no change.
Download zyraxon.ps1

Bash and zsh

Source one file in the shell you already have open and the provider becomes shell functions. Composes with pipes, so a log, a diff or a command's output can be handed straight to the model.

BashzshPipesTool calls
How to connect

Connection fields

Base URL
https://zyraxonai.lovable.app/api/public/zyraxon/v1
Model
zyraxon-apex

Any model id; zyraxon_models lists them live.

API key
zyraxon

Steps

  1. Download it: curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.sh -o zyraxon.sh.
  2. Source it in the current shell: . ./zyraxon.sh.
  3. Call zyraxon_ask "..." for an answer, or zyraxon_stream "..." to watch it arrive.
  4. Use zyraxon_ask -m <id> "..." for one turn.
  5. For tool calling, call zyraxon_agent "..."; it asks before writing or running anything.
  6. zyraxon_models lists every model the provider serves right now.

Code

Bash / zshbash
# Fetch it, then source it in the current shell
curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.sh -o zyraxon.sh
. ./zyraxon.sh

zyraxon_models                                    # any model, live
zyraxon_ask "explain what a race condition is"
zyraxon_stream "write a haiku about the sea"
zyraxon_ask -m zyraxon-apex "reason about this"
zyraxon_agent "create hello.py that prints hi, then run it"

# Work that may take hours, and survives anything
zyraxon_long -s refactor "rewrite each file in src, one at a time"
zyraxon_sessions                                  # inspect or resume a run

# Works in bash and zsh, and composes with a pipe
cat error.log | zyraxon_ask "what went wrong here?"
Pipe real outputbash
git diff HEAD~1 | zyraxon_ask "review this change and flag anything risky"
journalctl -p err -n 50 | zyraxon_ask "what broke here?"
  • •Works in bash and zsh; needs only curl and jq.
  • •Sourcing keeps it in your current shell, so no subshell and no PATH changes.
  • •zyraxon_models reads the list live, so any model the provider serves works with no change.
Download zyraxon.sh
Languages & Runtimes5

Go

The standard library is enough — no third-party client required. This works from a CLI tool, a service or a Go worker.

Gonet/http
How to connect

Code

net/httpgo
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"model": "zyraxon-apex",
		"messages": []map[string]string{
			{"role": "user", "content": "Plan my day."},
		},
	})

	req, _ := http.NewRequest("POST", "https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer zyraxon")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}

Rust

reqwest plus serde_json, which most Rust services already depend on. Streaming works the same way over the same URL.

Rustreqwest
How to connect

Code

reqwestrust
// reqwest + serde_json. No OpenAI crate needed.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let body = serde_json::json!({
        "model": "zyraxon-apex",
        "messages": [{ "role": "user", "content": "Plan my day." }]
    });

    let res = reqwest::Client::new()
        .post("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions")
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer zyraxon")
        .json(&body)
        .send()
        .await?
        .text()
        .await?;

    println!("{}", res);
    Ok(())
}

PHP

Plain cURL, so it drops into any PHP app, Laravel job, Symfony command or WordPress plugin without a client library.

PHPcURL
How to connect

Code

cURLphp
<?php
// Plain cURL, so it works from any PHP app, Laravel job or WordPress plugin.
$payload = json_encode([
    "model" => "zyraxon-apex",
    "messages" => [["role" => "user", "content" => "Plan my day."]],
]);

$ch = curl_init("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "Authorization: Bearer zyraxon",
    ],
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_RETURNTRANSFER => true,
]);
echo curl_exec($ch);
curl_close($ch);

Java, Kotlin and the JVM

The JDK's own HTTP client, so Spring Boot, a Gradle task, an Android app or a plain CLI can call the endpoint with no extra dependency.

JavaJVMSpring
How to connect

Code

HttpClientjava
// Java 11+ HttpClient. Works from Spring Boot, Android or a CLI tool.
var payload = """
  {"model":"zyraxon-apex","messages":[{"role":"user","content":"Plan my day."}]}
  """;

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions"))
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer zyraxon")
    .POST(HttpRequest.BodyPublishers.ofString(payload))
    .build();

var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

.NET and C#

HttpClient directly, or the OpenAI .NET SDK with a custom endpoint. Either way the request shape is the same one every other client sends.

.NETC#
How to connect

Code

HttpClientcsharp
// C# / .NET. The Azure OpenAI client and the OpenAI .NET SDK both take a custom endpoint.
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("Authorization", "Bearer zyraxon");

var json = """
  {"model":"zyraxon-apex","messages":[{"role":"user","content":"Plan my day."}]}
  """;

var res = await http.PostAsync(
    "https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions",
    new StringContent(json, System.Text.Encoding.UTF8, "application/json"));

Console.WriteLine(await res.Content.ReadAsStringAsync());
Bots & Home2

Discord, Slack and chat bots

A bot is one HTTP call. Take the message, send it as a user turn, post the reply back — the same pattern works for Discord, Slack, Telegram or a chat widget on your own site.

DiscordSlackBots
How to connect

Code

discord.jstypescript
// A Discord bot, a Slack app or any chat integration: it is one HTTP call.
import { Client, GatewayIntentBits } from "discord.js";

const client = new Client({ intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds] });

client.on("messageCreate", async (msg) => {
  if (msg.author.bot) return;

  const res = await fetch("https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions", {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: "Bearer zyraxon" },
    body: JSON.stringify({
      model: "zyraxon-apex",
      messages: [{ role: "user", content: msg.content }],
    }),
  });

  const data = await res.json();
  await msg.reply(data.choices[0].message.content);
});

client.login(process.env.DISCORD_TOKEN);
  • •Bot platforms have their own message-length limits. Truncate long replies or split them before posting.

Home Assistant, Node-RED and smart-home

Home automation hosts are HTTP clients with a templating layer, so a REST command or an HTTP request node is the whole integration.

Home AssistantNode-REDIoT
How to connect

Code

rest_commandyaml
# Home Assistant, Node-RED and similar automation hosts: use a REST command
# or an HTTP request node. In configuration.yaml:

rest_command:
  zyraxon_ask:
    url: "https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions"
    method: POST
    headers:
      Content-Type: application/json
      Authorization: "Bearer zyraxon"
    payload: >
      {"model": "zyraxon-apex",
       "messages": [{"role": "user", "content": "{{ prompt }}"}]}
Automation & Platforms3

n8n, Make and Zapier

No-code runners reach the endpoint through an HTTP request node. Point the node at the chat URL and map your previous step's output into the message content.

No codeHTTP node
How to connect

Code

HTTP request nodejson
// n8n: HTTP Request node (or the OpenAI node with a custom base URL)
// Method: POST
// URL:    https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions
// Header: Content-Type: application/json
// Header: Authorization: Bearer zyraxon
{
  "model": "zyraxon-apex",
  "messages": [{ "role": "user", "content": "{{ $json.prompt }}" }],
  "stream": false
}
  • •Set stream: false in a workflow runner. Most nodes buffer the body and cannot parse an SSE stream.

Postman, Insomnia and other HTTP clients

For exploring the API by hand before writing code. Save the request, then swap the model id to see how another ZYRAXON model answers the same turn.

Manual testing
How to connect

Code

Requesttext
POST https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions
Content-Type: application/json
Authorization: Bearer zyraxon

{
  "model": "zyraxon-apex",
  "messages": [{ "role": "user", "content": "Hello" }]
}
Discoverybash
# Anything that can read a JSON model list can configure itself.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[] | "\(.id)\t\(.context_window)\t\(.input_modalities | join(","))"'

# Provider health, capacity and overload policy:
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/provider | jq '{count, modalities, concurrency, overload}'

# The machine-readable integration catalogue (this whole page):
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/integrations | jq '.integrations[] | {id, name, category}'

Voice input and speech output

Speech-to-text and text-to-speech are both endpoints. Record on the device, transcribe it, send the transcript as a normal chat turn — then turn any answer back into speech, in the language the answer was written in, streamed as MP3 as it is synthesised.

Speech to textText to speech322 voices75 languages
How to connect

Connection fields

Speech-to-text
https://zyraxonai.lovable.app/api/public/stt
Text-to-speech
https://zyraxonai.lovable.app/api/public/tts
Voices
https://zyraxonai.lovable.app/api/public/tts/voices

Steps

  1. Record audio on the device and POST it to the speech-to-text endpoint as multipart form data.
  2. Send the transcript to the provider as an ordinary chat turn.
  3. To speak the answer, POST its text to the text-to-speech endpoint and play the MP3 it streams back.
  4. Pass locale (e.g. bn) or voice (e.g. bn-BD-NabanitaNeural) to choose the voice; without either, an English voice is used.
  5. Read the voices endpoint to offer a picker, or to match a voice to the language the answer came back in.

Code

Speak an answerbash
# The answer is spoken in the language you name, and the MP3 starts
# arriving before the sentence finishes being synthesised.
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from ZYRAXON.","locale":"en"}' \
  -o answer.mp3

# Play it
ffplay -nodisp -autoexit answer.mp3   # or: afplay / aplay / start
A language the answer is already inbash
# Bengali answer, Bengali voice — the endpoint picks it from the locale.
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"আমি একটি ZYRAXON মডেল।","locale":"bn"}' \
  -o bengali.mp3

# Or name the voice exactly
curl -s https://zyraxonai.lovable.app/api/public/tts \
  -H "Content-Type: application/json" \
  -d '{"text":"नमस्ते","voice":"hi-IN-SwaraNeural"}' -o hindi.mp3
Which voices existbash
curl -s https://zyraxonai.lovable.app/api/public/tts/voices | jq '{count, languages: (.languages|length)}'
curl -s https://zyraxonai.lovable.app/api/public/tts/voices | jq -r '.locales[]' | grep '^bn'
In a browser, play it as it streamstypescript
// The endpoint streams MP3, so the browser can start playing before the
// whole sentence has been synthesised.
const res = await fetch("https://zyraxonai.lovable.app/api/public/tts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: answer, locale: "en" }),
});
new Audio(URL.createObjectURL(await res.blob())).play();
Transcribe a recordingbash
curl -s https://zyraxonai.lovable.app/api/public/stt \
  -F "file=@recording.mp3" \
  -F "language=en" | jq -r '.text'
  • •Speech is opt-in: the helper offers a Listen button per answer, and nothing is spoken unless a visitor asks for it.
  • •The voice is chosen from the language you name, so a Bengali answer is read in Bengali rather than by an English voice.
  • •Code, links and Markdown are stripped before speaking — a reply that reads a code block aloud is unusable, so it says "a code block, 12 lines" instead.
  • •The engine is Microsoft Edge's Read Aloud, the same one behind the browser feature: 322 neural voices across 75 languages, no key to buy.
  • •To speak a reply, transcribe first and send text when the model does not accept audio. Check the model list for audio before sending an audio part.

Why a rooted provider

One name you control

Stable names

Upstream models come and go. Your model value does not have to change when they do.

Capacity pooling

Every model in the pool stands behind every brand, so one model's limit is absorbed rather than passed to you.

Built to hold a flood

Each request runs in its own lane with its own fingerprint, so one caller's rate limit never stalls another. Under overload a request is shed fast with a 503 and Retry-After instead of queueing behind a stranger — the lanes already running stay healthy.

Drop-in

Same request and response shapes as OpenAI, so existing code works with a base-URL change.