Using the SDK
Creating a client
DimeSchedulerClient is the single entry point. The constructor takes an options object:
import DimeSchedulerClient, { Environment } from "@dimescheduler/sdk";
const client = new DimeSchedulerClient({ apiKey: "MY_API_KEY" });
// Sandbox
const sandbox = new DimeSchedulerClient({
apiKey: "MY_API_KEY",
environment: Environment.Sandbox,
});
The default environment is production. Environment has two members: Production and Sandbox. It is a string enum where each value is the base URL, so you can pass any URL to point at a mock or a self-hosted instance:
const local = new DimeSchedulerClient({
apiKey: "test",
environment: "http://127.0.0.1:4010" as Environment,
});
Pass headers to attach extra headers to every request. The SDK sets X-API-KEY and User-Agent for you; anything you pass is merged on top:
const client = new DimeSchedulerClient({
apiKey: "MY_API_KEY",
headers: { "X-Tenant": "contoso" },
});
Re-use a single client across your app - it owns a fetch instance and a retry policy.
Domain accessors
Every entity hangs off a typed accessor on the client. Names mirror the .NET and Python SDKs 1:1, in idiomatic camelCase:
// CRUD: create, update, delete, getAll
client.categories // /category
client.resources // /resource
client.filterGroups // /filterGroup
client.filterValues // /filterValue
client.tags // /tag
client.projectMembers // /projectMember
client.taskDependencies // /taskDependency
// CRUD plus a purpose-built read
client.appointments // get(start, end, resources), getById(id)
client.jobs // get(jobNo)
client.tasks // get({ isOpen, page, pageSize })
client.timeEntries // get(start, end)
client.timeSessions // get(start, end)
client.notifications // get(page, limit, { sort, group, filter })
// create and delete only
client.taskAssignments // /taskAssignment
client.taskBudgetEntries // /taskBudgetEntry
// read-only: getAll
client.calendars // /calendar
client.resourceTypes // /resourcetype
client.appointmentFields // /appointmentField
client.appointmentDependencies // /appointmentDependency
// single-purpose
client.messages // send(message)
client.geocoding // geocode(address), geocodeText(address, country)
client.optimization // fieldService(), professionalServices(), dailyRoute()
CRUD-shaped entities expose create, update, delete, and getAll. Write methods accept either a single object or an array:
await client.categories.create(category);
await client.categories.create([cat1, cat2, cat3]);
await client.categories.update(category);
await client.categories.delete(category);
const { data } = await client.categories.getAll();
Endpoints with their own shape get purpose-built methods:
await client.appointments.get(startDate, endDate, ["R1", "R2"]);
await client.appointments.getById("2a77f6c6-c502-4ad2-9ee2-6fba0f9361a1");
await client.jobs.get("SO001");
await client.tasks.get({ isOpen: true, page: 1, pageSize: 50 });
await client.notifications.get(1, 50, { sort: "createdAt:desc" });
await client.messages.send({ text: "Heads up!", severity: 3 }); // 0 Verbose .. 5 Fatal
await client.geocoding.geocodeText("221B Baker Street", "GB");
await client.optimization.fieldService(request);
Message severity is numeric: 0 Verbose, 1 Debug, 2 Info, 3 Warning, 4 Error, 5 Fatal.
Every method takes an optional { signal } as its last parameter, so you can cancel an in-flight request with a standard AbortSignal. An aborted request is never retried:
const controller = new AbortController();
const pending = client.categories.getAll(undefined, { signal: controller.signal });
controller.abort();
For the full accessor list, see client.ts.
Models and types
Body and response types live under @dimescheduler/sdk/models. Write entities (Category, Resource, Appointment) match POST/PUT bodies; read DTOs (CategoryDto, ResourceDto) match GET responses:
import type { Category, CategoryDto } from "@dimescheduler/sdk/models";
For schemas without a re-export, every type is reachable via the generated components["schemas"] map exported from the package root:
import type { components } from "@dimescheduler/sdk";
type StoredProcedureData = components["schemas"]["Models.StoredProcedureData"];
Results
Every call returns { data, error, response } - the same shape as openapi-fetch, which the SDK is built on:
const { data, error, response } = await client.categories.getAll();
response.ok // boolean - HTTP 2xx
response.status // status code
data // typed body on success
error // typed error body on failure
The SDK never throws on a non-2xx - branch on response.ok (or error) and decide how to react:
const { data, error } = await client.categories.create(category);
if (error) {
logger.warn("Create failed", error);
return;
}
Typed errors
If you prefer exceptions, the package exports an error hierarchy that mirrors the .NET and Python SDKs: DimeSchedulerError (base, with statusCode, body and response) and the subclasses ValidationError (400/422), AuthenticationError (401), AuthorizationError (403), NotFoundError (404), ConflictError (409), RateLimitError (429, with retryAfter in seconds), ServerError (5xx) and NetworkError (no response received). classify(response, error) turns a failed result into the matching class without throwing; ensureOk(result) returns data on success and throws the classified error otherwise:
import { ensureOk, RateLimitError } from "@dimescheduler/sdk";
try {
const categories = ensureOk(await client.categories.getAll());
} catch (err) {
if (err instanceof RateLimitError) {
console.warn(`Rate limited, retry after ${err.retryAfter}s`);
} else {
throw err;
}
}
Retries
Transient failures retry by default. Responses with HTTP 408 / 429 / 500 / 502 / 503 / 504 are retried (retryStatusCodes), and so are network errors whose code is one of ECONNRESET, ECONNREFUSED, ETIMEDOUT, ENOTFOUND, EAI_AGAIN, EPIPE or UND_ERR_SOCKET. Any other error, including an AbortError, is thrown straight away.
The defaults are 3 retries (maxRetries), a 500 ms base delay (initialDelayMs) multiplied by 2 on every attempt (backoffMultiplier), and a 30,000 ms cap on any single delay (maxDelayMs). Backoff uses full jitter: the actual wait is drawn uniformly between 0 and the capped delay, so clients that fail at the same time do not retry in lockstep. With respectRetryAfter on (the default), a Retry-After header on the response replaces the computed backoff, still capped at maxDelayMs.
Override any of these via retry:
const client = new DimeSchedulerClient({
apiKey: "MY_API_KEY",
retry: { maxRetries: 5, initialDelayMs: 1000, maxDelayMs: 60000 },
});
Pass retry: false to disable retries entirely.
Middleware
The client is built on openapi-fetch, so you can intercept requests and responses:
client.use({
onRequest({ request }) {
request.headers.set("X-Trace-Id", crypto.randomUUID());
},
onResponse({ response }) {
metrics.observe(response.status);
},
});
Use client.eject(middleware) to remove a middleware.
Custom fetch
For runtimes that don't ship fetch, or when you want to plug in a wrapped fetch (instrumentation, retries, edge runtime helpers), pass fetch:
import nodeFetch from "node-fetch";
const client = new DimeSchedulerClient({
apiKey: "MY_API_KEY",
fetch: nodeFetch as unknown as typeof fetch,
});