Skip to main content

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