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): NodaroClientProp
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>.
| Resource | What it covers | Reference |
|---|---|---|
client.workflows | Workflows: create, update, share, export, import and run | Workflows and projects |
client.projects | The projects that hold workflows | Workflows and projects |
client.executions | Runs of a whole workflow | Jobs and executions |
client.jobs | Single generation jobs | Jobs and executions |
client.videoPro | Stop or continue a Generate Video Pro run | Jobs and executions |
client.nodes | The node catalog and single-node runs | Run nodes |
client.apps | Published apps and their runs | Apps and templates |
client.templates | The template marketplace | Apps and templates |
client.tutorials | Tutorial videos and tutorial workflows | Apps and templates |
client.llm | Structured output from a language model | LLM and Reduce |
client.reduce | Choose the best of many results, or combine them | LLM and Reduce |
client.uploads | File uploads | Media and uploads |
client.library | Your stored media | Media and uploads |
client.media | Download, trim, caption, overlay and collage media | Media and uploads |
client.voices | Voices, voice changer, voice design and dubbing | Voices and audio |
client.audio | Separate, isolate, mix, trim and transcribe audio | Voices and audio |
client.edit | Silence detection, audio sync, edit plans and EDL renders | Editing |
client.scene3d | Editable 3D scenes and 3D Render Pro | 3D scenes |
client.characters | Characters | Characters |
client.locations | Locations | Locations |
client.objects | Objects and props | Objects and creatures |
client.creatures | Animals and creatures | Objects and creatures |
client.community | The shared community library of assets | Community library |
client.studio | Studio productions | Studio productions |
client.shots | Shared shot records behind share links | Studio productions |
client.recast | Recast runs and authored scripts | Recast |
client.pipelines | Story-to-video pipelines | Pipelines |
client.copilot | Copilot threads, inside the Nodaro app only | Copilot |
client.models | The model catalog | Models and credits |
client.credits | Your balance and model prices | Models and credits |
client.pickerCatalogs | Valid options of each picker | Pickers, presets and prompts |
client.catalogs | Every picker catalog in one call | Pickers, presets and prompts |
client.presets | Saved and built-in node presets | Pickers, presets and prompts |
client.promptHelper | The Prompt Wizard | Pickers, presets and prompts |
client.organizations | Organizations, members and invitations | Organizations and workspaces |
client.workspaces | Workspaces, members and join codes | Organizations and workspaces |
client.developerApps | The OAuth apps you own | OAuth and developer apps |
client.oauth | Code exchange, token revocation and consent-screen data | OAuth 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 writeconst { data } = await client.workflows.get(id). Paginated lists add a cursor next todata, such asnextCursor. - Some resources return the payload. A few methods unwrap their response, for example
client.characters.list()resolves to{ characters, nextCursor }andclient.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
Jobuses snake_case fields, such asoutput_dataandcreated_at, because the API sends them that way. AWorkflowand aWorkflowExecutionuse 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)| Field | Type | Description |
|---|---|---|
id | string | The Nodaro user id. |
email | string | The user's email address. |
displayName | string | null | The display name, or null when it is not set. |
avatarUrl | string | null | The avatar URL, or null when it is not set. |
tier | string | The 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(). |
isAdmin | boolean | Whether 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 see | What it means | What to do |
|---|---|---|
| The fields are absent | The instance has no organizations | Do not show a workspace switcher. |
| The fields are present and empty | The account belongs to no organization | Offer to create or join one. |
organizationsUnavailable: true | The lookup failed | Keep the selection you already had. Do not tell the user they lost access. |
withWorkspace(workspaceId)
withWorkspace(workspaceId: string | null): NodaroClientReturns 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 spaceThe 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
Responseobjects from a mock. - Retries. Wrap the global
fetchin 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
supabaseAuthis 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'sOriginheader already names your app. AclientLabelyou set yourself is always sent.
Frequently asked questions
Related
TypeScript SDK
Authentication
Errors
Organizations and workspaces
Types
Last updated on
TypeScript SDK
Install @nodaro/sdk, authenticate with an API token, and run Nodaro nodes and workflows from TypeScript in Node.js, the browser and edge runtimes.
Authentication
Choose how the Nodaro SDK authenticates, with StaticTokenAuth, CallbackAuth or supabaseAuth, and share one browser login across your subdomains.