Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch
TypeScript SDK

Client

Create a Nodaro SDK client with createClient, set its base URL, auth, timeout and workspace, and find every resource the client exposes.

The client is the object createClient() returns: a NodaroClient that holds your base URL, your auth provider and your settings, and exposes every part of the Nodaro API as a resource, such as client.workflows or client.nodes. You create it once and reuse it for every call.

Create a client

import { createClient, StaticTokenAuth } from "@nodaro/sdk"

const client = createClient({
  baseUrl: "https://app.nodaro.ai",
  auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
  timeoutMs: 120_000,
})
createClient(options: ClientOptions): NodaroClient

Prop

Type

NodaroClient is also exported as a class, so you can type a function that receives a client:

import type { NodaroClient } from "@nodaro/sdk"

async function countWorkflows(client: NodaroClient, projectId: string) {
  const { data } = await client.workflows.list({ projectId })
  return data.length
}

Resources on the client

Every resource is created by createClient and reached as client.<resource>.

ResourceWhat it coversReference
client.workflowsWorkflows: create, update, share, export, import and runWorkflows and projects
client.projectsThe projects that hold workflowsWorkflows and projects
client.executionsRuns of a whole workflowJobs and executions
client.jobsSingle generation jobsJobs and executions
client.videoProStop or continue a Generate Video Pro runJobs and executions
client.nodesThe node catalog and single-node runsRun nodes
client.appsPublished apps and their runsApps and templates
client.templatesThe template marketplaceApps and templates
client.tutorialsTutorial videos and tutorial workflowsApps and templates
client.llmStructured output from a language modelLLM and Reduce
client.reduceChoose the best of many results, or combine themLLM and Reduce
client.uploadsFile uploadsMedia and uploads
client.libraryYour stored mediaMedia and uploads
client.mediaDownload, trim, caption, overlay and collage mediaMedia and uploads
client.voicesVoices, voice changer, voice design and dubbingVoices and audio
client.audioSeparate, isolate, mix, trim and transcribe audioVoices and audio
client.editSilence detection, audio sync, edit plans and EDL rendersEditing
client.scene3dEditable 3D scenes and 3D Render Pro3D scenes
client.charactersCharactersCharacters
client.locationsLocationsLocations
client.objectsObjects and propsObjects and creatures
client.creaturesAnimals and creaturesObjects and creatures
client.communityThe shared community library of assetsCommunity library
client.studioStudio productionsStudio productions
client.shotsShared shot records behind share linksStudio productions
client.recastRecast runs and authored scriptsRecast
client.pipelinesStory-to-video pipelinesPipelines
client.copilotCopilot threads, inside the Nodaro app onlyCopilot
client.modelsThe model catalogModels and credits
client.creditsYour balance and model pricesModels and credits
client.pickerCatalogsValid options of each pickerPickers, presets and prompts
client.catalogsEvery picker catalog in one callPickers, presets and prompts
client.presetsSaved and built-in node presetsPickers, presets and prompts
client.promptHelperThe Prompt WizardPickers, presets and prompts
client.organizationsOrganizations, members and invitationsOrganizations and workspaces
client.workspacesWorkspaces, members and join codesOrganizations and workspaces
client.developerAppsThe OAuth apps you ownOAuth and developer apps
client.oauthCode exchange, token revocation and consent-screen dataOAuth and developer apps

The client itself has three more methods: me(), withWorkspace() and request().

What methods return

  • Envelopes are kept. When an endpoint answers { "data": ... }, the method resolves to that envelope, so you write const { data } = await client.workflows.get(id). Paginated lists add a cursor next to data, such as nextCursor.
  • Some resources return the payload. A few methods unwrap their response, for example client.characters.list() resolves to { characters, nextCursor } and client.credits.balance() to the balance itself. Each reference page shows the exact return type.
  • Deletes and cancels usually resolve to { success: true }.
  • Field names follow the wire format. A Job uses snake_case fields, such as output_data and created_at, because the API sends them that way. A Workflow and a WorkflowExecution use camelCase.

Every response and input type is exported, so you can import it with import type. See Types.

me()

me(): Promise<UserIdentity & MeOrganizations>

Returns the identity behind the current token (GET /v1/me). Any valid token resolves to its owner, whether it is an API token, an OAuth access token or a browser session. A missing or invalid token throws UnauthorizedError.

const me = await client.me()
console.log(me.email, me.tier)
FieldTypeDescription
idstringThe Nodaro user id.
emailstringThe user's email address.
displayNamestring | nullThe display name, or null when it is not set.
avatarUrlstring | nullThe avatar URL, or null when it is not set.
tierstringThe stored subscription tier, such as "free" or "pro". For the tier that is actually enforced, including pay-as-you-go, read effectiveTier from client.credits.balance().
isAdminbooleanWhether the user is an administrator. Use it only to decide what to show. The server checks every permission itself.

On a Nodaro Cloud instance with organizations, the result also carries organizations, workspaces, lastWorkspaceId and organizationsUnavailable. Treat their three states differently:

What you seeWhat it meansWhat to do
The fields are absentThe instance has no organizationsDo not show a workspace switcher.
The fields are present and emptyThe account belongs to no organizationOffer to create or join one.
organizationsUnavailable: trueThe lookup failedKeep the selection you already had. Do not tell the user they lost access.

withWorkspace(workspaceId)

withWorkspace(workspaceId: string | null): NodaroClient

Returns a new client that acts in workspaceId. The new client shares the auth, base URL, timeout and fetch of the original. Pass null for your personal space.

const classroom = client.withWorkspace(workspaceId)

await classroom.workflows.run(workflowId) // runs in the workspace
await client.workflows.run(workflowId)    // runs in the personal space

The method returns a new client instead of changing the current one. Two operations that run at the same time on one client can therefore never mix up their workspaces.

The workspace decides scope, never access. It chooses which workspace a list reads from and where a new item is created. Reading, changing, deleting or running an item you name by id depends on that item's own workspace. A forgotten workspace cannot hide your work, and a wrong one cannot reach anyone else's.

Workspaces belong to organizations on Nodaro Cloud. See Organizations and workspaces and Workspaces.

request(method, path, options)

request<T>(method: string, path: string, options?: {
  body?: unknown
  query?: Record<string, string | number | boolean | undefined>
  headers?: Record<string, string>
  signal?: AbortSignal
}): Promise<T>

Sends a request to any endpoint, for the few that have no resource method yet. It adds your auth header and your workspace, sends body as JSON, applies timeoutMs, and throws the same typed errors as the resource methods.

// The same request that client.jobs.list() sends
const page = await client.request<{ data: unknown[]; next: string | null }>("GET", "/v1/jobs", {
  query: { type: "llm-structured", limit: 20 },
})

A FormData body is sent as a multipart upload. Query values that are undefined are left out. The REST API reference lists every endpoint and its fields.

Timeouts and a custom fetch

timeoutMs aborts a request that takes longer than the limit, 60 seconds by default. Most generation takes longer than any sensible HTTP timeout, so start it and poll the job instead: client.nodes.runAndWait() does both for you. The streaming methods, client.copilot.stream() and client.media.downloadVideoProgress(), do not apply the timeout, because they are meant to stay open for minutes.

Pass your own fetch to change how requests travel:

  • Tests. Return canned Response objects from a mock.
  • Retries. Wrap the global fetch in a helper that retries on a 5xx answer.
  • Tracing. Wrap it with your tracing or monitoring library.
const client = createClient({
  baseUrl: "https://app.nodaro.ai",
  auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
  fetch: (input, init) => tracedFetch(input, init),
})

Runtimes and browsers

The SDK uses only fetch and URL, which are global in Node.js 20 and newer, in modern browsers, in React Native, on Cloudflare Workers, on Deno and on Bun. No polyfill is needed.

  • CORS with OAuth tokens. A browser app that calls Nodaro with an OAuth access token must run on an origin listed in the developer app's allowedOrigins. See OAuth and developer apps.
  • CORS with a session. A browser app that uses supabaseAuth is not checked against that list.
  • The client label. On a server, the SDK sends X-Nodaro-Client: sdk/<version>, and Nodaro records it as the origin of each job. In a browser the default label is not sent, because the browser's Origin header already names your app. A clientLabel you set yourself is always sent.

Frequently asked questions

Last updated on

On this page