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.
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.
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.
https://zyraxonai.lovable.app/api/public/sttSend 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.
https://zyraxonai.lovable.app/api/public/ttsVideo, 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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completionsThe 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/v1OpenAI 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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completionsThe 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.
| Field | Required | What it does |
|---|---|---|
model | yes | Any id from the model catalogue, e.g. zyraxon-apex. |
messages | yes | The usual [{role, content}] array. |
stream | no | true for server-sent events. |
tools | no | OpenAI function-tool definitions; the model calls them. |
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
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
modelvalue is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/modelsWhat 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.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[] | .id'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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completionsThe 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.
| Field | Required | What it does |
|---|---|---|
model | yes | Any id from the model catalogue, e.g. zyraxon-apex. |
messages | yes | The usual [{role, content}] array. |
stream | no | true for server-sent events. |
tools | no | OpenAI function-tool definitions; the model calls them. |
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
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
modelvalue is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
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.
https://zyraxonai.lovable.app/api/public/sttSend 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.
| Field | Required | What it does |
|---|---|---|
language | yes | bn or bn-BD. Required in both modes — the recogniser does not detect language, so a guess would be a wrong transcript. |
file | no | Batch, multipart: the audio. Format is read from the bytes, so a mislabelled file still works. |
audio | no | Batch, JSON: base64 audio, with an optional format. |
segment | no | Live: seconds per committed chunk, default 4. |
partial | no | Live: seconds between partial updates, default 1.2. |
# 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 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 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 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}`);
}
}"""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']}")#!/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.
languageis 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.GETon this URL returns the contract as JSON, so a coding assistant handed the URL can read it without a human explaining it.
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.
https://zyraxonai.lovable.app/api/public/ttsTurn 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.
| Field | Required | What it does |
|---|---|---|
text | yes | What to say, up to 5000 characters. Markdown is stripped first. |
voice | no | An exact name from the voices list, e.g. bn-BD-NabanitaNeural. Wins over locale. |
locale | no | A language, e.g. bn. Picks a voice in that language. |
# 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
// 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
voiceexactly also skips the voice-catalogue lookup, so the first byte of audio arrives sooner.
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.
/api/public/zyraxon/v1/chat/completionsOverload 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.
/api/public/zyraxon/v1/providerRun 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.
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.
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.
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.
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.
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.
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.
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.
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 Matrix
Sees images, reasons at scale
Vision + text · 1M context · deep reasoning
ZYRAXON Pulse
Built for speed on long inputs
Low latency · 262K context · 262K output
ZYRAXON Titan
The long-output workhorse
1M context · 512K output · image + video
ZYRAXON Neural
Balanced general intelligence
Reasoning · 200K context · 32K output
ZYRAXON Codex
Writes and repairs code
Code + vision · 200K context
ZYRAXON Visionary
Reads images, video and documents
Image + video + PDF · 200K context
ZYRAXON Cyber
Every input modality, one model
Text + image + audio + video · 200K context
ZYRAXON Quantum
Million-token document reasoning
1M context · vision · 131K output
ZYRAXON Genesis
Fresh from the lab
Latest experimental reasoning · every modality
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.
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
}'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.
{
"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,..." } }
]
}]
}{
"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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completionsThe 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.
| Field | Required | What it does |
|---|---|---|
model | yes | Any id from the model catalogue, e.g. zyraxon-apex. |
messages | yes | The usual [{role, content}] array. |
stream | no | true for server-sent events. |
tools | no | OpenAI function-tool definitions; the model calls them. |
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
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
modelvalue is a ZYRAXON name, not an upstream one. That name does not change when the pool behind it does.
https://zyraxonai.lovable.app/api/public/zyraxon/v1/modelsWhat 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.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[] | .id'https://zyraxonai.lovable.app/api/public/ttsTurn 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.
| Field | Required | What it does |
|---|---|---|
text | yes | What to say, up to 5000 characters. Markdown is stripped first. |
voice | no | An exact name from the voices list, e.g. bn-BD-NabanitaNeural. Wins over locale. |
locale | no | A language, e.g. bn. Picks a voice in that language. |
# 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
// 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
voiceexactly also skips the voice-catalogue lookup, so the first byte of audio arrives sooner.
https://zyraxonai.lovable.app/api/public/sttSend 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.
| Field | Required | What it does |
|---|---|---|
language | yes | bn or bn-BD. Required in both modes — the recogniser does not detect language, so a guess would be a wrong transcript. |
file | no | Batch, multipart: the audio. Format is read from the bytes, so a mislabelled file still works. |
audio | no | Batch, JSON: base64 audio, with an optional format. |
segment | no | Live: seconds per committed chunk, default 4. |
partial | no | Live: seconds between partial updates, default 1.2. |
# 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 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 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 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}`);
}
}"""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']}")#!/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.
languageis 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.GETon this URL returns the contract as JSON, so a coding assistant handed the URL can read it without a human explaining it.
Also available
/api/public/zyraxon/v1/providerWhat the serving pool looks like right now — modalities, concurrency, and each model's live status and context window.
Fields and example
https://zyraxonai.lovable.app/api/public/zyraxon/v1/providerWhat 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 }.
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.
/api/public/zyraxon/v1/integrationsThe same integration list this page shows, as JSON — the exact values to paste into a host app, with steps and snippets.
Fields and example
https://zyraxonai.lovable.app/api/public/zyraxon/v1/integrationsThe 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 }] }.
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/integrations | jq -r '.integrations[] | "\(.id)\t\(.name)"'/api/public/tts/voicesEvery voice available, with its locale and gender. 322 voices across 75 languages.
Fields and example
https://zyraxonai.lovable.app/api/public/tts/voicesEvery voice available, with its locale and gender. 322 voices across 75 languages.
Returns { count, languages, locales, data: [{ name, locale, gender, friendly }] }.
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-TanishaaNeuralAnywhere
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/v1Default model
zyraxon-apexAPI key
zyraxonTermux (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.
How to connect
Steps
- Install Termux from F-Droid (the Play Store build is abandoned).
- Run the setup script:
curl -fsSL https://zyraxonai.lovable.app/integrations/termux-setup.sh | bash - Open a new shell, or run
source ~/.bashrc, sozyraxonis on your PATH. - Run
zyraxonwith no arguments to open the chat box, then just type. - Use
/modelsand/modelto pick any model the provider serves, and/agent onto let it read, write and run commands on the device.
Code
$ 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
byeCommands 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.# 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?" | zyraxonpkg 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'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.
/newclears 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-lockbefore a long agent turn.
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.
How to connect
Steps
- Install the two tools the client needs:
sudo apt install -y curl jq. - Export
ZYRAXON_BASE,ZYRAXON_MODELandZYRAXON_KEY, or paste them into~/.bashrc. - Download
kali-ask.sh,chmod +xit, and put it on yourPATH. - Ask something:
zyraxon-ask "explain the output of this nmap scan". - On NetHunter, start the chroot with
nethunterfirst, then do the same inside it.
Code
# 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'#!/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: 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'# 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/envwith mode 600 andsourceit, rather than typing them inline.
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.
How to connect
Code
// 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);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'];// 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())
}// 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.
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.
How to connect
Connection fields
zyraxonopenai/zyraxon-apexCopy 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.
https://zyraxonai.lovable.app/api/public/zyraxon/v1Copy this exactly, including /v1 at the end.
zyraxonAny non-empty value, unless the operator set ZYRAXON_PROVIDER_KEY.
Steps
- Open OpenHands and go to Settings, then the LLM (model / provider) section.
- 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. - Choose Custom Model.
- For Custom Model, copy
openai/zyraxon-apexexactly. Theopenai/prefix is what LiteLLM reads to pick a handler; without it OpenHands fails before it calls us, withLLM Provider NOT provided. - For Base URL, copy
https://zyraxonai.lovable.app/api/public/zyraxon/v1exactly. If this box is missing, Advanced is not on yet. - For API Key, use any non-empty value —
zyraxonhere. - Press Save Changes, then start a conversation.
Code
Name (Optional) zyraxon
Custom Model openai/zyraxon-apex
Base URL https://zyraxonai.lovable.app/api/public/zyraxon/v1
API Key zyraxonOpenHands 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. 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 rightIf 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"# 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 /modelspublishes 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, putopenai/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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxon-apexzyraxonSteps
- Find the provider or model settings in the app.
- Choose the OpenAI-compatible / custom option.
- Paste
https://zyraxonai.lovable.app/api/public/zyraxon/v1as the base URL andzyraxon-apexas the model. - Put
zyraxonin the key field if the app insists on one. - Reload the app so it re-fetches the model list.
Code
{
"models": [
{
"title": "ZYRAXON",
"provider": "openai",
"model": "zyraxon-apex",
"apiBase": "https://zyraxonai.lovable.app/api/public/zyraxon/v1",
"apiKey": "zyraxon"
}
]
}export OPENAI_API_BASE="https://zyraxonai.lovable.app/api/public/zyraxon/v1"
export OPENAI_API_KEY="zyraxon"
aider --model openai/zyraxon-apex# 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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxonzyraxon-apexSteps
- Open the extension's settings and choose the OpenAI Compatible provider.
- Paste
https://zyraxonai.lovable.app/api/public/zyraxon/v1as the base URL andzyraxon-apexas the model id. - Put
zyraxonin the key field if the extension insists on one. - Reload the window so the extension re-reads the model list.
Code
// 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 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"// 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.
How to connect
Code
// 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.
How to connect
Code
# 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.
How to connect
Code
// 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"]
}
}
}
]
}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.
How to connect
Code
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
}'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'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.
How to connect
Code
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 ?? "");
}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)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)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 });
}
}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.argumentsis 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.
How to connect
Code
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.
How to connect
Code
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)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.
How to connect
Connection fields
openai/zyraxon-apexThe openai/ prefix is required by LiteLLM.
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxonSteps
- Use the model string with its provider prefix:
openai/zyraxon-codex. Without a prefix LiteLLM stops before it ever calls us. - Set the API base to
https://zyraxonai.lovable.app/api/public/zyraxon/v1and the key to any non-empty value. - For a gateway like Open WebUI, add a custom OpenAI connection with those same values and reload.
- To front the whole line with LiteLLM's own proxy, use the config file below.
Code
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.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# 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# 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-codexandzyraxon/codexall resolve to the same model. - •
drop_params: truein 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/modelstring.
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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxon-apexAny model id; ai.models() lists them live.
zyraxonSteps
- Download the client:
curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.py -o zyraxon.py. - In a shell, import it:
from zyraxon import Zyraxon. - Call
ai.ask(...)for a whole answer, or loop overai.stream(...)to print as it arrives. - Ask a follow-up on the same
aiand it remembers the conversation; callai.reset()to start fresh. - Use
ai.models()to see every model the provider serves right now, thenZyraxon(model=...)to pick one. - For tool calling, pass
ai.agent(prompt, tools, run)your own tools — the loop is handled for you.
Code
# 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# 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 stepfrom 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))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 installis 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/sessionsand 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.
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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxon-apexAny model id; Get-ZyraxonModels lists them live.
zyraxonSteps
- Download it:
Invoke-WebRequest https://zyraxonai.lovable.app/integrations/zyraxon.ps1 -OutFile zyraxon.ps1. - Dot-source it in your session:
. .\zyraxon.ps1. - Call
Invoke-Zyraxon "..."for an answer, or add-Streamto watch it arrive. - Use
-Model <id>for one turn, orSet-ZyraxonConfig -Model <id>for the session. - For tool calling, call
Invoke-ZyraxonAgent "..." -Confirmso it asks before writing or running anything. Get-ZyraxonModelslists every model the provider serves right now.
Code
# 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-pulseGet-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 -Confirmasks before any write or command; without it, it runs unattended. - •
Get-ZyraxonModelsreads the list live, so any model the provider serves works with no change.
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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/zyraxon/v1zyraxon-apexAny model id; zyraxon_models lists them live.
zyraxonSteps
- Download it:
curl -fsSL https://zyraxonai.lovable.app/integrations/zyraxon.sh -o zyraxon.sh. - Source it in the current shell:
. ./zyraxon.sh. - Call
zyraxon_ask "..."for an answer, orzyraxon_stream "..."to watch it arrive. - Use
zyraxon_ask -m <id> "..."for one turn. - For tool calling, call
zyraxon_agent "..."; it asks before writing or running anything. zyraxon_modelslists every model the provider serves right now.
Code
# 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?"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
curlandjq. - •Sourcing keeps it in your current shell, so no subshell and no PATH changes.
- •
zyraxon_modelsreads the list live, so any model the provider serves works with no change.
Go
The standard library is enough — no third-party client required. This works from a CLI tool, a service or a Go worker.
How to connect
Code
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.
How to connect
Code
// 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.
How to connect
Code
<?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.
How to connect
Code
// 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.
How to connect
Code
// 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());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.
How to connect
Code
// 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.
How to connect
Code
# 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 }}"}]}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.
How to connect
Code
// 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: falsein 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.
How to connect
Code
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" }]
}# 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.
How to connect
Connection fields
https://zyraxonai.lovable.app/api/public/stthttps://zyraxonai.lovable.app/api/public/ttshttps://zyraxonai.lovable.app/api/public/tts/voicesSteps
- Record audio on the device and POST it to the speech-to-text endpoint as multipart form data.
- Send the transcript to the provider as an ordinary chat turn.
- To speak the answer, POST its text to the text-to-speech endpoint and play the MP3 it streams back.
- Pass
locale(e.g.bn) orvoice(e.g.bn-BD-NabanitaNeural) to choose the voice; without either, an English voice is used. - Read the voices endpoint to offer a picker, or to match a voice to the language the answer came back in.
Code
# 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# 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.mp3curl -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'// 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();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
audiobefore 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.