SDKs
No ZAITRACK SDK is published: there is no @zaitrack/sdk on npm, no zaitrack on PyPI and no Go module.
Call the API with any HTTP client. It is plain JSON over HTTPS with a bearer token; the
API reference gives every operation's fields.
The helpers below are all a client needs: authentication, an idempotency key on writes, problems as errors and cursor paging. Webhook verification is in Verify a delivery.
TypeScript
Node.js 18 or later, or any runtime with fetch:
const BASE = "https://api.zaitrack.app";
const TOKEN = process.env.ZAITRACK_API_KEY!; // zk_test_… or zk_live_…
export class ZaitrackError extends Error {
constructor(public status: number, public problem: Record<string, unknown>) {
super(`${status} ${String(problem.code ?? "")}: ${String(problem.detail ?? problem.title ?? "")}`);
}
}
export async function call<T>(method: string, path: string, body?: unknown, ifMatch?: number): Promise<T> {
const headers: Record<string, string> = { Authorization: `Bearer ${TOKEN}` };
if (body !== undefined) headers["Content-Type"] = "application/json";
if (method !== "GET") headers["Idempotency-Key"] = crypto.randomUUID();
if (ifMatch !== undefined) headers["If-Match"] = `"${ifMatch}"`;
const res = await fetch(BASE + path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
if (res.status === 204) return undefined as T;
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new ZaitrackError(res.status, data);
return data as T;
}
export async function* pages<T>(path: string): AsyncGenerator<T> {
let cursor = "";
do {
const sep = path.includes("?") ? "&" : "?";
const page = await call<{ items: T[]; nextCursor?: string }>("GET", `${path}${sep}cursor=${encodeURIComponent(cursor)}`);
yield* page.items;
cursor = page.nextCursor ?? "";
} while (cursor);
}
// for await (const trip of pages<{ id: string }>("/v1/trips?pageSize=100")) console.log(trip.id);
A retry of a failed write should reuse the same Idempotency-Key: pass it in rather than generating one when you
retry.
Python
With requests:
import os
import uuid
import requests
BASE = "https://api.zaitrack.app"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['ZAITRACK_API_KEY']}"
class ZaitrackError(Exception):
def __init__(self, status: int, problem: dict):
super().__init__(f"{status} {problem.get('code', '')}: {problem.get('detail') or problem.get('title', '')}")
self.status, self.problem = status, problem
def call(method: str, path: str, body=None, if_match: int | None = None, idempotency_key: str | None = None):
headers = {}
if method != "GET":
headers["Idempotency-Key"] = idempotency_key or str(uuid.uuid4())
if if_match is not None:
headers["If-Match"] = f'"{if_match}"'
res = session.request(method, BASE + path, json=body, headers=headers, timeout=30)
if res.status_code == 204:
return None
data = res.json() if res.content else {}
if not res.ok:
raise ZaitrackError(res.status_code, data)
return data
def pages(path: str, **params):
cursor = ""
while True:
page = call("GET", path + "?" + requests.compat.urlencode({**params, "cursor": cursor}))
yield from page["items"]
cursor = page.get("nextCursor") or ""
if not cursor:
return
# for trip in pages("/v1/trips", pageSize=100): print(trip["id"])
Go
The standard library is enough: build an http.Request, set Authorization, Content-Type and
Idempotency-Key, and decode the body into your own structs, or into map[string]any for the problem on a
non-2xx answer.