Use the TypeScript SDK
Goal
You'll install the official @dq-cloud/sdk package, authenticate with a personal API key, and run three worked examples — listing datasources, running a validation, and following its execution runs — without writing any HTTP code yourself.
Prereqs
- Node 18 or newer (or any runtime with a global
fetch: modern browsers, Cloudflare Workers, Deno, Vercel Edge). - A running Measured Cloud instance and an org you can see in the UI.
- A personal API key — mint one under Settings → API keys (see Create an API token). The key starts with
dqk_. Agent tokens (dqa_) work too; JWT session tokens do not.
Install
npm install @dq-cloud/sdk
The package targets Node 18+ and any environment with a global fetch. It ships ESM and CJS builds with TypeScript declaration files. Zero runtime dependencies.
Authenticate
The SDK takes the API key and the base URL of your install:
import { DQCloudClient } from "@dq-cloud/sdk";
const client = new DQCloudClient({
apiKey: "dqk_...",
baseUrl: "https://dq.example.com",
});
429 and 503 responses are retried automatically with exponential backoff (plus jitter). Pass retryCount: 0 to opt out, or tune backoffBaseMs / backoffCapMs to fit your environment.
Browser caveat: putting a
dqk_directly in shipped browser code exposes it to anyone with devtools. For browser-side use, scope the key narrowly or proxy through your own backend. Read-only public dashboards that already accept anonymous traffic are fine.
Example 1 — list datasources
import { DQCloudClient } from "@dq-cloud/sdk";
const orgId = "11111111-1111-1111-1111-111111111111";
const client = new DQCloudClient({
apiKey: "dqk_...",
baseUrl: "https://dq.example.com",
});
for (const ds of await client.datasources.list({ orgId })) {
console.log(ds.name, ds.type, ds.execution_mode);
}
If the key is wrong you'll see an AuthError. If the org doesn't exist (or your key isn't scoped to it) you'll see NotFoundError.
Example 2 — run a validation and wait
Validations are exposed as client.validations, and their runs as client.executionRuns:
import { DQCloudClient, DQCloudError, EXECUTION_RUN_FAILED_STATUSES } from "@dq-cloud/sdk";
const client = new DQCloudClient({
apiKey: "dqk_...",
baseUrl: "https://dq.example.com",
});
try {
const run = await client.validations.run("validation-uuid", {
orgId: "org-uuid",
workspaceId: "ws-uuid",
wait: true,
timeoutMs: 300_000,
});
console.log("status:", run.status, "health:", run.health_score);
if (EXECUTION_RUN_FAILED_STATUSES.has(run.status)) process.exitCode = 1;
} catch (err) {
if (err instanceof DQCloudError) {
console.error("could not start the run:", err.message);
}
throw err;
}
wait: true polls the execution run every 2 000 ms (change it with pollIntervalMs) and throws if it hasn't finished within timeoutMs. A failed run is returned, not thrown, so check its status. Without wait, the call returns as soon as the run is queued, with its execution_run_id and parent_result_id; follow it later with client.executionRuns.wait(...). Pass rule_ids to run only some of the validation's rules. Stop a run with client.validations.cancelRun(validationId, { orgId, workspaceId, parentResultId }).
To check one table, run its default validation: every data asset with rules has one.
Example 3 — list a validation's recent runs
import { DQCloudClient } from "@dq-cloud/sdk";
const client = new DQCloudClient({ apiKey: "dqk_...", baseUrl: "https://dq.example.com" });
const page = await client.executionRuns.list("validation-uuid", {
orgId: "org-uuid",
workspaceId: "ws-uuid",
limit: 10,
});
for (const run of page.data) {
console.log(run.id, run.trigger, run.status, run.health_score);
}
Each run records what started it (trigger: manual, schedule, api, asset or rule). client.executionRuns.results(runId, { orgId, workspaceId }) returns a run's per-asset and per-rule results.
Deprecated:
client.checkpoints. Checkpoints were retired (see Checkpoints (retired)).client.checkpointsandclient.results.forCheckpointstill work for one release: a checkpoint'srunruns the validation it maps to and returns itsexecution_run_id, and checkpoint writes throw without calling the API (the server answers them with410 Gone). They are removed in the next major release — switch toclient.validationsandclient.executionRuns.
Example 4 — add a rule to an asset
Rules are exposed as client.rules. A rule lives on its data asset; it always joins the asset's default validation, and validation_ids adds it to other validations too:
const rule = await client.rules.create("asset-uuid", {
orgId: "org-uuid",
workspaceId: "ws-uuid",
data: {
rule_type: "expect_column_values_to_not_be_null",
kwargs: { column: "order_id" },
validation_ids: ["validation-uuid"],
},
});
const { data: rules } = await client.rules.list({
orgId: "org-uuid",
workspaceId: "ws-uuid",
validationId: "validation-uuid",
});
const run = await client.rules.run("asset-uuid", rule.id, {
orgId: "org-uuid",
workspaceId: "ws-uuid",
wait: true,
});
client.rules.list filters by dataAssetId or validationId and pages with limit / cursor; get returns one rule with the validation_ids that hold it, update edits a rule in place, delete removes it, and setValidations sets which validations hold it.
Deprecated:
client.ruleSuites. Rule suites were retired (see Rule suites (retired)). For one releaseclient.ruleSuitesreads the API's read-only alias (list,get,validate); its writes throw without calling the API (the server answers them with410 Gone). Switch toclient.rules.
Verify
npm ls @dq-cloud/sdkreports a version starting at0.1.0.npx tsx examples/01-list-datasources.ts(in the SDK repo) prints your datasources.- Errors raise typed exceptions (
AuthError,NotFoundError,RateLimitError,ValidationFailedError,ServerError) — yourcatchclauses don't need to inspectresponse.status.
Caveats
- Streaming endpoints (data-docs blobs, large export downloads) aren't exposed yet. Use a plain
fetchcall for those for now. - Token revocation isn't instant on the server (HARDEN-7 cache). A revoked key may keep working for up to
TOKEN_CACHE_TTL_SECONDS(default 5 seconds) after revocation. - For browser builds, prefer wrapping the SDK in a server-side proxy so your API key never reaches the client.