Guides
Calling your endpoints from code
A small, typed client for published endpoints, with version pinning, retries and error handling.
Published endpoints are plain HTTPS: POST https://www.scalaix.com/<project>/<collection>/<endpoint> with {"inputs": {...}}. This guide wraps that in a reusable client. Each collection's generated docs show the exact inputs and output.
TypeScript
scalaix.ts
type Usage = { input_tokens: number; output_tokens: number; cost_usd: number };
type Result<T> = { data: T; model: string; version: number; cached: boolean; usage: Usage; request_id: string };
export class ScalaixError extends Error {
constructor(public status: number, public type: string, message: string, public requestId?: string) {
super(message);
}
}
const RETRY = new Set([429, 502, 504]);
export async function callEndpoint<TInputs, TData>(
path: string, // "acme/support/classify-ticket"
inputs: TInputs,
{ version, attempts = 3 }: { version?: number; attempts?: number } = {},
): Promise<Result<TData>> {
const url = `https://www.scalaix.com/${path}${version ? `?version=${version}` : ""}`;
for (let i = 0; ; i++) {
const res = await fetch(url, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SCALAIX_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ inputs }),
});
if (res.ok) return res.json();
const body = await res.json().catch(() => ({ error: { type: "unknown", message: res.statusText } }));
if (!RETRY.has(res.status) || i + 1 >= attempts) {
throw new ScalaixError(res.status, body.error.type, body.error.message, res.headers.get("x-scalaix-request-id") ?? undefined);
}
const wait = Number(res.headers.get("retry-after")) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000 + Math.random() * 250));
}
}Use it with types that match your output schema:
type TicketInputs = { ticket: { subject: string; body: string }; language?: "English" | "Spanish" };
type TicketData = { category: "billing" | "bug" | "other"; priority: number; tags?: string[] };
const { data, version } = await callEndpoint<TicketInputs, TicketData>(
"acme/support/classify-ticket",
{ ticket: { subject: "Charged twice", body: "Billed two times in March." } },
{ version: 3 },
);Python
scalaix.py
import os, random, time
import requests
class ScalaixError(Exception):
def __init__(self, status, type_, message, request_id=None):
super().__init__(f"{status} {type_}: {message}")
self.status, self.type, self.request_id = status, type_, request_id
def call_endpoint(path, inputs, version=None, attempts=3, timeout=90):
url = f"https://www.scalaix.com/{path}"
params = {"version": version} if version else None
headers = {"Authorization": f"Bearer {os.environ['SCALAIX_API_KEY']}"}
for i in range(attempts):
r = requests.post(url, params=params, headers=headers, json={"inputs": inputs}, timeout=timeout)
if r.ok:
return r.json()
if r.status_code not in (429, 502, 504) or i + 1 == attempts:
err = r.json().get("error", {})
raise ScalaixError(r.status_code, err.get("type"), err.get("message"), r.headers.get("x-scalaix-request-id"))
time.sleep(float(r.headers.get("retry-after", 2 ** i)) + random.random() / 4)Practices
- Pin versions in production. Publish, test the new version with
?version=nfrom staging, then move the pin. - Treat 422 as a bug in your code, not a transient error: the message has the input path that's wrong.
- Handle 502
invalid_model_outputby logging the request id. If it repeats, add field descriptions or pick a stronger model in the endpoint. - Give each service its own key, limited to
endpointsmode and the collections it needs, with a budget.