Reference@node-data/sdk@0.4.0

SDK reference

Every namespace and method on the client. Signatures are copied from sdk/src, not paraphrased. Start with the overview if you haven't installed it yet.

every import
import {
NodeData,
// webhook helpers
verifyRequest, verifyWebhook, signPayload, SIGNATURE_HEADER,
// errors
ApiError, AuthenticationError, BadRequestError, ConflictError,
ConnectionError, NodeDataError, NotFoundError, PaymentRequiredError,
PermissionError, RateLimitError, ServerError, TimeoutError,
WebhookVerificationError,
// values
SCOPES, WEBHOOK_EVENT_TYPES, DEFAULT_BASE_URL, SDK_VERSION,
// pagination
Page,
} from "@node-data/sdk";

NodeData

new NodeData(options?)

The client. Reads NODE_DATA_API_KEY and NODE_DATA_BASE_URL from the environment when the corresponding options are omitted.

nd.mode

Getter: "test", "live", or "unknown", derived from the key prefix without a network call.

KeyMode | "unknown"

nd.maskedKey

Getter: the public prefix plus dots. Safe to log.

string

nd.baseUrl

Getter: the API root this client is pointed at.

string

nd.ping()

One round trip that confirms the key works and reports the account slug and scopes. Never throws — failures come back as { ok: false, error }.

Promise<{ ok, latencyMs, account?, scopes?, error? }>

TS
const health = await nd.ping();
if (!health.ok) process.exit(1);
nd.request(method, path, { query?, body?, options? })

Escape hatch for endpoints the SDK doesn't model yet. Same auth, retries, and error mapping as the typed methods.

Promise<T>

TS
const data = await nd.request<{ items: unknown[] }>(
"GET",
"/some/new/endpoint",
{ query: { limit: 10 } },
);

nd.models

Marketplace listings

Models, policies, workflows, and datasets — one underlying resource, distinguished by `type`.

list(params?, options?)models:read

A page of published listings. Filter with q, type, category; paginate with limit and cursor. The returned Page is async-iterable over every subsequent page.

Promise<Page<Asset>>

TS
const page = await nd.models.list({ q: "grasping", limit: 50 });
for await (const asset of page) console.log(asset.slug);
iterate(params?, options?)models:read

The same walk as an async generator, when you'd rather not hold the first Page.

AsyncGenerator<Asset>

retrieve(idOrSlug, options?)models:read

One published listing by id or URL slug. Unpublished listings are not addressable.

Promise<Asset>

create(params, options?)models:upload

Publishes a listing. price_cents is integer cents — 0 for free, ≥100 for paid. Attach a file by uploading out of band first and passing storage_path.

Promise<Asset>

TS
const asset = await nd.models.create({
title: "Bin-Picking Policy v2",
description: "Diffusion policy trained on 40k grasps.",
type: "policy",
price_cents: 4900,
});
update(idOrSlug, params, options?)listings:write

Edits metadata, price, or publish state on a listing you own. Another account's listing reads as 404, not 403.

Promise<Asset>

del(idOrSlug, options?)listings:write

Unpublishes a listing. Purchase history and buyer download rights are preserved.

Promise<{ id, deleted }>

nd.datasets

Dataset listings

A thin wrapper over models that defaults `type` to "dataset". Same shapes, dataset scopes.

list(params?, options?)datasets:read

Published datasets, cursor-paginated.

Promise<Page<Asset>>

retrieve(idOrSlug, options?)datasets:read

One dataset by id or slug.

Promise<Asset>

create(params, options?)datasets:upload

Publishes a dataset — `type` is set for you.

Promise<Asset>

nd.account

Account & usage

Who this key belongs to, and what it has spent.

retrieve(options?)

The authenticated account plus the key's mode and scopes. An empty scopes array means unrestricted.

Promise<Account>

usage({ days? }, options?)

Metered inference spend over a window (1–365 days, default 30), rolled up and per model. Costs come back in USD micros and dollars.

Promise<Usage>

TS
const usage = await nd.account.usage({ days: 7 });
console.log(usage.summary.cost_usd);

nd.payouts

Sales & payouts

Completed sales for listings you authored, and the 25/75 split.

retrieve({ limit? }, options?)payouts:read

Sales plus an all-time summary. payouts_paused distinguishes 'nothing owed' from 'owed but not moving' — balances keep accruing while it's true.

Promise<Payouts>

TS
const payouts = await nd.payouts.retrieve({ limit: 100 });
console.log(payouts.summary.net_cents / 100, "USD");

nd.webhookEndpoints

Webhook endpoints

Manage delivery targets. Every method needs webhooks:write; secrets are returned only on create.

list(options?)webhooks:write

All endpoints on the account. Never includes secrets.

Promise<WebhookEndpoint[]>

create(params, options?)webhooks:write

Registers an endpoint and returns its signing secret exactly once. Unknown event types are silently dropped, so read it back to confirm.

Promise<WebhookEndpoint>

TS
const endpoint = await nd.webhookEndpoints.create({
url: "https://example.com/hooks",
events: ["listing.purchased"],
});
await store.set("secret", endpoint.secret!); // shown once
retrieve(id, options?)webhooks:write

One endpoint's current configuration.

Promise<WebhookEndpoint>

update(id, params, options?)webhooks:write

Change the URL, replace the subscription list, or set enabled: false to pause without losing history.

Promise<WebhookEndpoint>

del(id, options?)webhooks:write

Permanently removes the endpoint and stops all deliveries.

Promise<{ id, deleted }>

deliveries(id, { limit? }, options?)webhooks:write

Recent delivery attempts with response status and attempt count. First place to look when a handler goes quiet.

Promise<WebhookDelivery[]>

nd.inference

Metered inference

OpenAI-compatible completions. Requires a premium key with inference:run.

models(options?)

The Node model catalog with context windows and live per-token pricing. Fetch this rather than hardcoding prices.

Promise<InferenceModel[]>

create(params, options?)inference:run

One buffered completion. Always set max_tokens — it's the only hard ceiling on a runaway generation.

Promise<ChatCompletion>

stream(params, options?)inference:run

Streams parsed SSE chunks. Never retried mid-flight, and given a long default timeout.

AsyncGenerator<ChatCompletionChunk>

TS
for await (const chunk of nd.inference.stream({ model, messages })) {
process.stdout.write(chunk.choices[0]?.delta.content ?? "");
}
streamToText(params, { onToken? })inference:run

Collects a stream into the full assistant message while optionally handing you each token as it arrives.

Promise<string>

Webhook helpers

Top-level exports · Web Crypto only

These use Web Crypto rather than Node built-ins, so the same code runs on Node 18+, Bun, Deno, Workers, and Vercel Functions. Verification enforces a timestamp tolerance to neutralize replays and accepts multiple v1 values so secret rotation doesn't drop deliveries.

verifyRequest(request, secret, { tolerance? })

Verifies a standard Request and returns the parsed event. Reads the raw body itself, so you can't accidentally verify re-serialized JSON.

Promise<WebhookEvent<T>>

TS
import { verifyRequest } from "@node-data/sdk";
export async function POST(req: Request) {
const event = await verifyRequest(req, process.env.ND_WEBHOOK_SECRET!);
return new Response(null, { status: 204 });
}
verifyWebhook({ payload, signature, secret, tolerance?, now? })

The lower-level form, for frameworks that hand you the body and headers separately. Default tolerance is 300 seconds.

Promise<WebhookEvent<T>>

signPayload(payload, secret, timestamp?)

Produces an nd-signature header value. Useful for testing your own handler without waiting on a real delivery.

Promise<string>

SIGNATURE_HEADER

The header name constant: "nd-signature".

string

Error classes

Catch the specific case you can handle and re-throw the rest. catch gives you unknown under modern configs, so instanceof is how you get typed access.

ClassStatusNotes
NodeDataErrorBase class for everything the SDK throws.
ApiErroranyBase for non-2xx. Carries status, code, requestId, body.
BadRequestError400Validation or malformed request.
AuthenticationError401Missing, malformed, revoked, or expired key.
PaymentRequiredError402Premium key not yet activated.
PermissionError403Valid key, missing scope. Exposes requiredScope.
NotFoundError404No such resource, or not yours.
ConflictError409Conflicting state. Retryable.
RateLimitError429Too fast. Exposes retryAfter in seconds.
ServerError5xxPlatform-side failure. Retryable.
ConnectionErrorNo HTTP response at all: DNS, offline, abort.
TimeoutErrorExceeded the configured timeout. Subclass of ConnectionError.
WebhookVerificationErrorSignature failed. code is malformed_signature, timestamp_out_of_tolerance, or signature_mismatch.

Options

NodeDataOptions

OptionDefaultNotes
apiKeyprocess.env.NODE_DATA_API_KEYThrows if neither is set.
baseUrlhttps://www.nodedata.ai/api/v1Or NODE_DATA_BASE_URL.
timeout60000Milliseconds, per attempt.
maxRetries2429/5xx/network only. Streams force 0.
headers{}Merged over the SDK defaults.
fetchglobalThis.fetchSwap in for tests, proxies, or tracing.
onRequestCalled before every attempt with { method, url, attempt }.
dangerouslyAllowBrowserfalseLive keys in a browser throw without it. Trusted internal tools only.

RequestOptions (per call)

TS
await nd.models.list(
{ limit: 100 },
{
timeout: 5_000, // override the client timeout
maxRetries: 0, // fail fast instead of retrying
headers: { "x-trace": traceId },
signal: controller.signal, // cancellation
},
);

Page<T>

TS
const page = await nd.models.list();
page.items; // T[] — this page only
page.hasMore; // boolean
page.nextCursor; // string | null
await page.next(); // Page<T> | null
await page.all(500); // T[] — walk with a ceiling
for await (const item of page) { /* every page, lazily */ }

Missing an endpoint?

New endpoints ship before SDK methods do. Use nd.request() in the meantime — it goes through the same auth, retry, and error mapping as everything above, so you don't lose anything but types.