TypeScript and Python SDKs
Typed clients generated from the OpenAPI contract: how to use them today from the repository, with booking, error, pagination and idempotency examples.
Updated:
Wagend has two SDKs that are generated from the contract packages/openapi/openapi.yaml, so their types always follow the API.
The SDKs are not published yet on npm or PyPI (Coming soon). Today you use them from the repository, as explained below. If you would rather not depend on them, the REST API works with any HTTP client.
TypeScript
The client (packages/sdk-ts) is a light wrapper over fetch with types for every route.
Install from the repository
cd packages/sdk-ts
npm ci
npm run build # genera dist/
In your project, install it from that folder:
npm install /path/to/wagendapp/packages/sdk-ts
Book a slot
import { createWagendClient, WagendError } from "@wagend/sdk";
const api = createWagendClient({
baseUrl: "https://api.wagend.app/v1",
apiKey: process.env.WAGEND_API_KEY!, // wg_live_xxx
});
try {
const { data: services } = await api.GET("/services");
const serviceId = services!.data![0].id!;
const { data: slots } = await api.GET("/slots", {
params: { query: { service_id: serviceId, from: "2026-10-15T08:00:00-03:00", to: "2026-10-15T13:00:00-03:00" } },
});
const slot = slots!.data[0];
// 10-minute hold
const { data: hold } = await api.POST("/holds", {
params: { header: { "Idempotency-Key": crypto.randomUUID() } },
body: { service_id: serviceId, start: slot.start, party_size: 1, resource_ids: slot.resource_ids },
});
// Confirmation with the customer data
const { data: booking } = await api.POST("/holds/{id}/confirm", {
params: { path: { id: hold!.id! }, header: { "Idempotency-Key": crypto.randomUUID() } },
body: { customer: { name: "Carlos", phone: "+5491155550000", locale: "es" } },
});
console.log(booking!.status); // "confirmed"
} catch (err) {
if (err instanceof WagendError) {
console.error(err.status, err.code, err.retryAfter);
} else {
throw err;
}
}
- Authentication: the client adds
Authorization: Bearerfor you. - Idempotency: on every write it adds an
Idempotency-Keyif you do not pass one. To retry an operation, pass the same key yourself in both attempts (valid for 24 hours). The TypeScript type declares it inparams.headerfor the routes that require it. - Errors: any non-2xx response throws
WagendErrorwith.status,.code,.problem(RFC 9457) and.retryAfter(seconds, on 429).
Pagination
let cursor: string | undefined;
do {
const { data: page } = await api.GET("/bookings", { params: { query: { limit: 50, cursor } } });
for (const booking of page?.data ?? []) console.log(booking.id);
cursor = page?.next_cursor ?? undefined;
} while (cursor);
Customers and actions
// Search and create customers (scopes customers:read and customers:write)
const { data: found } = await api.GET("/customers", { params: { query: { q: "carlos" } } });
await api.POST("/customers", { body: { name: "Ana", phone: "+5491155551111", tags: ["vip"] } });
// Run a stage action, for example assigning a delivery to a courier (scope bookings:write).
// "atribuir" is the key of that action in the deliveries template.
await api.POST("/bookings/{id}/actions/{key}", {
params: { path: { id: taskId, key: "atribuir" }, header: { "Idempotency-Key": crypto.randomUUID() } },
body: { data: {}, resource_id: courierResourceId },
});
Action keys (key) are defined by your business's stage configuration: look them up with GET /stage-config.
Python
The client (packages/sdk-py) uses httpx and generated Pydantic v2 models.
Install from the repository
pip install /path/to/wagendapp/packages/sdk-py
# or, with uv:
uv pip install /path/to/wagendapp/packages/sdk-py
Book a slot
from datetime import UTC, datetime
from wagend import Wagend, WagendError
with Wagend("https://api.wagend.app/v1", "wg_live_xxx") as api:
try:
service = api.list_services()[0]
slots = api.list_slots(
str(service.id), datetime(2026, 10, 15, 8, tzinfo=UTC), datetime(2026, 10, 15, 13, tzinfo=UTC)
)
hold = api.request(
"POST", "/holds",
json={"service_id": str(service.id), "start": slots[0].start.isoformat(), "party_size": 1},
idempotency_key="booking-carlos-2026-10-15",
)
booking = api.request(
"POST", f"/holds/{hold['id']}/confirm",
json={"customer": {"name": "Carlos", "phone": "+5491155550000"}},
)
print(booking["status"])
except WagendError as err:
print(err.status, err.code, err.retry_after)
list_services()andlist_slots()return typed models. For the other routes useapi.request(method, path, params=…, json=…); you can validate the response withwagend.models.- Idempotency: writes carry an
Idempotency-Key(one is generated if you do not passidempotency_key). Retry with the same key. - Errors:
WagendErrorwith.status,.code,.problemand.retry_after(on 429). - Pagination:
api.paginate("/bookings", params={"limit": 50})walks every page.
for booking in api.paginate("/bookings", params={"limit": 50}):
print(booking["id"])
Rate limits
The limit is per key and per project. On a 429, wait retry_after / retryAfter seconds before retrying. See Conventions.
Regenerating the SDKs
If the contract changes, make sdk regenerates both from openapi.yaml and make sdk-check fails if they are out of date.