SDK de TypeScript y Python

Clientes tipados generados desde el contrato OpenAPI: cómo usarlos hoy desde el repositorio, con ejemplos de reserva, errores, paginación e idempotencia.

Actualizado:

Wagend tiene dos SDK que se generan a partir del contrato packages/openapi/openapi.yaml, así que sus tipos siempre siguen la API.

Los SDK todavía no están publicados en npm ni en PyPI (Próximamente). Hoy se usan desde el repositorio, como se explica abajo. Si preferís no depender de ellos, la API REST funciona con cualquier cliente HTTP.

TypeScript

El cliente (packages/sdk-ts) es un envoltorio liviano sobre fetch con tipos de todas las rutas.

Instalar desde el repositorio

cd packages/sdk-ts
npm ci
npm run build          # genera dist/

En tu proyecto, instalalo desde esa carpeta:

npm install /ruta/a/wagendapp/packages/sdk-ts

Reservar un horario

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];

  // Pre-reserva de 10 minutos
  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 },
  });

  // Confirmación con los datos del cliente
  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;
  }
}
  • Autenticación: el cliente agrega Authorization: Bearer solo.
  • Idempotencia: en toda escritura agrega una Idempotency-Key si no pasás una. Para reintentar una operación, pasá vos la misma clave en los dos intentos (vale 24 horas). El tipo de TypeScript la declara en params.header para las rutas que la exigen.
  • Errores: toda respuesta que no sea 2xx lanza WagendError con .status, .code, .problem (RFC 9457) y .retryAfter (segundos, en 429).

Paginación

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);

Clientes y acciones

// Buscar y crear clientes (scopes customers:read y customers:write)
const { data: found } = await api.GET("/customers", { params: { query: { q: "carlos" } } });
await api.POST("/customers", { body: { name: "Ana", phone: "+5491155551111", tags: ["vip"] } });

// Ejecutar una acción de la etapa, por ejemplo asignar una entrega a un repartidor (scope bookings:write).
// "atribuir" es la clave de esa acción en el modelo de entregas.
await api.POST("/bookings/{id}/actions/{key}", {
  params: { path: { id: taskId, key: "atribuir" }, header: { "Idempotency-Key": crypto.randomUUID() } },
  body: { data: {}, resource_id: courierResourceId },
});

Las claves de acción (key) las define la configuración de etapas de tu negocio: consultalas con GET /stage-config.

Python

El cliente (packages/sdk-py) usa httpx y modelos Pydantic v2 generados.

Instalar desde el repositorio

pip install /ruta/a/wagendapp/packages/sdk-py
# o, con uv:
uv pip install /ruta/a/wagendapp/packages/sdk-py

Reservar un horario

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="reserva-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() y list_slots() devuelven modelos tipados. Para el resto de las rutas usá api.request(método, ruta, params=…, json=…); podés validar la respuesta con wagend.models.
  • Idempotencia: las escrituras llevan Idempotency-Key (se genera una si no pasás idempotency_key). Reintentá con la misma clave.
  • Errores: WagendError con .status, .code, .problem y .retry_after (en 429).
  • Paginación: api.paginate("/bookings", params={"limit": 50}) recorre todas las páginas.
for booking in api.paginate("/bookings", params={"limit": 50}):
    print(booking["id"])

Límites de uso

El límite es por clave y por proyecto. Ante un 429, esperá retry_after / retryAfter segundos antes de reintentar. Ver Convenciones.

Regenerar los SDK

Si cambia el contrato, make sdk regenera los dos desde openapi.yaml y make sdk-check falla si quedaron desactualizados.