Automation and HTTP Clients
n8n, Make, Zapier and Postman reach the endpoint through a plain HTTP request node.
No-code runners are ordinary HTTP clients, so they need nothing beyond the chat URL and a JSON body. The one thing to remember is to turn streaming off: most workflow nodes buffer the whole response and cannot parse an event stream.
n8n / Make / Zapier
json
// 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-codex",
"messages": [{ "role": "user", "content": "{{ $json.prompt }}" }],
"stream": false
}⚠
Set stream to false
A workflow node that buffers the body will choke on SSE frames. Use
"stream": false and read choices[0].message.content.Postman and Insomnia
text
POST https://zyraxonai.lovable.app/api/public/zyraxon/v1/chat/completions
Content-Type: application/json
Authorization: Bearer zyraxon
{
"model": "zyraxon-codex",
"messages": [{ "role": "user", "content": "Hello" }]
}Python tool-calling loop
The Python equivalent of the TypeScript loop on the SDKs page, for a runner written in 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-codex", 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})Discovery, from a runner
An automation can read the endpoint's own description of itself, so it does not need to be updated when the model line changes.
shell
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/models | jq -r '.data[].id'
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/provider | jq '{modalities, overload, usage}'
curl -s https://zyraxonai.lovable.app/api/public/zyraxon/v1/integrations | jq '.integrations[] | {id, name, category}'★
Self-describing by design
/models, /provider and /integrations are all public and CORS-open, so a runner can configure itself at startup instead of hardcoding what ZYRAXON serves today.