Develop
SDKs
Wardengate ships first-party SDKs for the languages we see most in platform teams: TypeScript for Node.js, Python, Go, and Java. Every SDK wraps the same REST API, produces typed models, retries on transient errors, and honours server-side rate limits without you thinking about it.
Supported languages
- TypeScript / Node.js —
@wardengate/sdk, pure ESM, Node 20+ and modern runtimes (Bun, Deno, Cloudflare Workers). - Python —
wardengate-sdkon PyPI, Python 3.10+, full type annotations, synchronous andasyncclients. - Go —
github.com/wardengate/wardengate-go, module-aware, context-first APIs. - Java —
io.wardengate:wardengate-sdk, JDK 17+, suitable for Spring and Micronaut applications.
Authentication
Two mechanisms are supported by every SDK: a static API token, or OIDC client credentials exchanged for a short-lived access token. The second option is the better default when your workload runs in a platform (Kubernetes, EKS, GKE) that can issue workload identity tokens. Both mechanisms read from environment variables by default so the same code runs locally and in CI without branching.
TypeScript / Node.js
npm install @wardengate/sdk
# or
pnpm add @wardengate/sdk
yarn add @wardengate/sdkThe package is pure ESM and ships its own .d.ts files. Every resource has a named type export so you can annotate your own function signatures — for example import type { Session, Policy } from "@wardengate/sdk".
import { Wardengate } from "@wardengate/sdk";
const wg = new Wardengate({
host: "https://wardengate.example.com",
// Reads WG_API_TOKEN from process.env by default.
// apiToken: process.env.WG_API_TOKEN,
});
// OIDC client-credentials alternative:
// const wg = new Wardengate({
// host: "https://wardengate.example.com",
// oidc: {
// issuer: "https://id.example.com",
// clientId: process.env.WG_CLIENT_ID!,
// clientSecret: process.env.WG_CLIENT_SECRET!,
// },
// });List sessions
const page = await wg.sessions.list({
state: "active",
limit: 50,
});
for (const session of page.data) {
console.log(session.id, session.user, session.target);
}Error handling
import { WardengateError, RateLimitError } from "@wardengate/sdk";
try {
await wg.policies.get("pol_missing");
} catch (err) {
if (err instanceof WardengateError) {
console.error(err.code, err.message, err.requestId);
if (err.code === "policy.not_found") {
// handle gracefully
}
} else if (err instanceof RateLimitError) {
console.error("retry after", err.retryAfterSeconds, "s");
} else {
throw err;
}
}Pagination
for await (const session of wg.sessions.listAll({ state: "active" })) {
// Auto-follows nextCursor; stops when the server returns null.
console.log(session.id);
}Python
pip install wardengate-sdkThe Python SDK exposes both a synchronous Wardengate class and an AsyncWardengate class with the same surface area; pick whichever matches your caller. Both use httpx under the hood.
from wardengate import Wardengate
wg = Wardengate(
host="https://wardengate.example.com",
# reads WG_API_TOKEN from the environment
)
# OIDC client-credentials alternative:
# from wardengate import OIDCCredentials
# wg = Wardengate(
# host="https://wardengate.example.com",
# credentials=OIDCCredentials(
# issuer="https://id.example.com",
# client_id=os.environ["WG_CLIENT_ID"],
# client_secret=os.environ["WG_CLIENT_SECRET"],
# ),
# )List sessions
page = wg.sessions.list(state="active", limit=50)
for session in page.data:
print(session.id, session.user, session.target)
# Auto-paginated iterator:
for session in wg.sessions.list_all(state="active"):
print(session.id)Error handling
from wardengate import WardengateError, RateLimitError
try:
wg.policies.get("pol_missing")
except WardengateError as err:
print(err.code, err.message, err.request_id)
except RateLimitError as err:
print("retry after", err.retry_after_seconds)Go
go get github.com/wardengate/wardengate-go@latestEvery call takes a context.Context as its first argument so timeouts and cancellation are uniform. Optional fields use pointers — helpers like wg.String, wg.Int, wg.Bool take away the address-of boilerplate.
package main
import (
"context"
"log"
wg "github.com/wardengate/wardengate-go"
)
func main() {
client, err := wg.NewClient(wg.Options{
Host: "https://wardengate.example.com",
// Reads WG_API_TOKEN from the environment by default.
})
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
page, err := client.Sessions.List(ctx, wg.SessionListParams{
State: wg.String("active"),
Limit: 50,
})
if err != nil {
log.Fatal(err)
}
for _, s := range page.Data {
log.Printf("%s %s %s", s.ID, s.User, s.Target)
}
}Error handling
if err != nil {
var apiErr *wg.APIError
if errors.As(err, &apiErr) {
log.Printf("code=%s msg=%s req=%s", apiErr.Code, apiErr.Message, apiErr.RequestID)
}
var rateErr *wg.RateLimitError
if errors.As(err, &rateErr) {
time.Sleep(rateErr.RetryAfter)
}
}Java
<!-- Maven -->
<dependency>
<groupId>io.wardengate</groupId>
<artifactId>wardengate-sdk</artifactId>
<version>1.6.2</version>
</dependency>
// Gradle
implementation("io.wardengate:wardengate-sdk:1.6.2")The Java SDK uses the JDK 17 HttpClient and Jackson. Models are immutable records; builders are generated so configuring a call reads the same as configuring a bean.
import io.wardengate.Wardengate;
import io.wardengate.model.Session;
import io.wardengate.model.SessionListParams;
import io.wardengate.Page;
Wardengate wg = Wardengate.builder()
.host("https://wardengate.example.com")
// Reads WG_API_TOKEN from the environment by default.
.build();
Page<Session> page = wg.sessions().list(
SessionListParams.builder().state("active").limit(50).build()
);
for (Session s : page.data()) {
System.out.println(s.id() + " " + s.user() + " " + s.target());
}Pagination
All list endpoints are cursor-paginated. Every SDK exposes two shapes: a single-page call that returns one Page object with a nextCursor, and an iterator that follows the cursor until it exhausts results. Prefer the iterator in application code; prefer the single-page call in UI code where you drive pagination from the user.
Rate limit handling
When the server returns HTTP 429 with a Retry-After header, the SDKs sleep for the indicated duration and retry transparently up to three times. Requests that still fail surface a typed RateLimitError (or the language's equivalent) so callers can back off further.
// The SDKs respect Retry-After automatically for up to three attempts.
// Override the cap if your workload is bursty:
const wg = new Wardengate({
host: "https://wardengate.example.com",
maxRetries: 5,
// Cap the total time the SDK will spend retrying a single call.
retryBudgetMs: 30_000,
});Error handling model
Every SDK maps the REST error envelope onto a typed exception with fields for code, message, requestId, and a free-form details map. Build your conditional logic on the code — never on the message, which is for humans — and always log requestId so support can find the server-side trace in one lookup.
Versioning policy
SDK versions follow semver and track the API version they target. A patch release is bug-fix only; a minor release adds new resources and optional fields; a major release may change a type signature. The server pins every token to a specific api_version at creation time, so old SDK releases keep working even as the surface grows. When a breaking API change is on the roadmap the release notes point at a migration SDK version first.
Community SDKs
Community-maintained SDKs exist for Ruby, PHP, Rust, and .NET. These are not built or reviewed by the Wardengate team, and their release cadence is not tied to the server. Read the source before you depend on one, especially for anything that handles tokens or secrets — we cannot vouch for third-party code.
Next steps
- API reference — the underlying HTTP surface and error contract.
- Webhooks — if you are polling the SDK for events, a webhook is almost certainly the better shape.
- CLI reference — the fastest way to sanity-check what an SDK call will do against a live server.