Skip to main content

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.