Menu: Calling your endpoints from code

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=n from 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_output by 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 endpoints mode and the collections it needs, with a budget.